home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 49 / Amiga Format CD49 (2000-01-17)(Future Publishing)(GB)(Track 1 of 3)[!][issue 2000-02].iso / -serious- / graphics / gnuplot / gnuplot-3.7.1src / gnuplot-3.7.1 / graphics.c < prev    next >
C/C++ Source or Header  |  1999-11-29  |  132KB  |  4,519 lines

  1. #ifndef lintt->vector) (xlefSid = "$Id: graphics.c,v 1.24.2.2 1999/09/15 15:30:29 lhecking Exp $";
  2. #endif
  3.  
  4. /* GNUPLOT - graphics.c */
  5.  
  6. /*[
  7.  * Copyright 1986 - 1993, 1998   Thomas Williams, Colin Kelley
  8.  *
  9.  * Permission to use, copy, and distribute this software and its
  10.  * documentation for any purpose with or without fee is hereby granted,
  11.  * provided that the above copyright notice appear in all copies and
  12.  * that both that copyright notice and this permission notice appear
  13.  * in supporting documentation.
  14.  *
  15.  * Permission to modify the software is granted, but not the right to
  16.  * distribute the complete modified source code.  Modifications are to
  17.  * be distributed as patches to the released version.  Permission to
  18.  * distribute binaries produced by compiling modified sources is granted,
  19.  * provided you
  20.  *   1. distribute the corresponding source modifications from the
  21.  *    released version in the form of a patch file along with the binaries,
  22.  *   2. add special version identification to distinguish your version
  23.  *    in addition to the base release version number,
  24.  *   3. provide your name and address as the primary contact for the
  25.  *    support of your modified version, and
  26.  *   4. retain our contact information in regard to use of the base
  27.  *    software.
  28.  * Permission to distribute the released version of the source code along
  29.  * with corresponding source modifications in the form of a patch file is
  30.  * granted with same provisions 2 through 4 for binary distributions.
  31.  *
  32.  * This software is provided "as is" without express or implied warranty
  33.  * to the extent permitted by applicable law.
  34. ]*/
  35.  
  36.  
  37. #include "plot.h"
  38. #include "setshow.h"
  39.  
  40. /* key placement is calculated in boundary, so we need file-wide variables
  41.  * To simplify adjustments to the key, we set all these once [depends on
  42.  * key_reverse] and use them throughout.
  43.  */
  44.  
  45. /*{{{  local and global variables */
  46. static int key_sample_width;    /* width of line sample */
  47. static int key_sample_left;    /* offset from x for left of line sample */
  48. static int key_sample_right;    /* offset from x for right of line sample */
  49. static int key_point_offset;    /* offset from x for point sample */
  50. static int key_text_left;    /* offset from x for left-justified text */
  51. static int key_text_right;    /* offset from x for right-justified text */
  52. static int key_size_left;    /* size of left bit of key (text or sample, depends on key_reverse) */
  53. static int key_size_right;    /* size of right part of key (including padding) */
  54.  
  55. /* I think the following should also be static ?? */
  56.  
  57. static int key_xl, key_xr, key_yt, key_yb;    /* boundarys for key field */
  58. static int max_ptitl_len = 0;    /* max length of plot-titles (keys) */
  59. static int ktitl_lines = 0;    /* no lines in key_title (key header) */
  60. static int ptitl_cnt;        /* count keys with len > 0  */
  61. static int key_cols;        /* no cols of keys */
  62. static int key_rows, key_col_wth, yl_ref;
  63.  
  64.  
  65. /* penalty for doing tics by callback in gen_tics is need for
  66.  * global variables to communicate with the tic routines
  67.  * Dont need to be arrays for this
  68.  */
  69. static int tic_start, tic_direction, tic_text, rotate_tics, tic_hjust, tic_vjust, tic_mirror;
  70.  
  71. /* set by tic_callback - how large to draw polar radii */
  72. static double largest_polar_circle;
  73.  
  74. /* either xformat etc or invented time format
  75.  * index with FIRST_X_AXIS etc
  76.  * global because used in gen_tics, which graph3d also uses
  77.  */
  78. char ticfmt[8][MAX_ID_LEN+1]; /* HBB 990106: fix buffer overrun */
  79. int timelevel[8];
  80. double ticstep[8];
  81.  
  82. static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
  83.  
  84. static int key_entry_height;    /* bigger of t->v_size, pointsize*t->v_tick */
  85. static int p_width, p_height;    /* pointsize * { t->h_tic | t->v_tic } */
  86.  
  87.  
  88. /* there are several things on right of plot - key, y2tics and y2label
  89.  * when working out boundary, save posn of y2label for later...
  90.  * Same goes for x2label.
  91.  * key posn is also stored in key_xl, and tics go at xright
  92.  */
  93. static int ylabel_x, y2label_x, xlabel_y, x2label_y, title_y, time_y, time_x;
  94. static int ylabel_y, y2label_y, xtic_y, x2tic_y, ytic_x, y2tic_x;
  95. /*}}} */
  96.  
  97. /*{{{  static fns and local macros */
  98. static void plot_impulses __PROTO((struct curve_points * plot, int yaxis_x,
  99.                    int xaxis_y));
  100. static void plot_lines __PROTO((struct curve_points * plot));
  101. static void plot_points __PROTO((struct curve_points * plot));
  102. static void plot_dots __PROTO((struct curve_points * plot));
  103. static void plot_bars __PROTO((struct curve_points * plot));
  104. static void plot_boxes __PROTO((struct curve_points * plot, int xaxis_y));
  105. static void plot_vectors __PROTO((struct curve_points * plot));
  106. static void plot_f_bars __PROTO((struct curve_points * plot));
  107. static void plot_c_bars __PROTO((struct curve_points * plot));
  108.  
  109. static void edge_intersect __PROTO((struct coordinate GPHUGE * points, int i,
  110.                     double *ex, double *ey));
  111. static int two_edge_intersect __PROTO((struct coordinate GPHUGE * points,
  112.                     int i, double *lx, double *ly));
  113. static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
  114.  
  115. static void plot_steps __PROTO((struct curve_points * plot));    /* JG */
  116. static void plot_fsteps __PROTO((struct curve_points * plot));    /* HOE */
  117. static void plot_histeps __PROTO((struct curve_points * plot));    /* CAC */
  118. static void histeps_horizontal __PROTO((int *xl, int *yl, double x1, double x2,
  119.                     double y));             /* CAC */
  120. static void histeps_vertical __PROTO((int *xl, int *yl, double x, double y1,
  121.                     double y2));        /* CAC */
  122. static void edge_intersect_steps __PROTO((struct coordinate GPHUGE * points,
  123.                 int i, double *ex, double *ey)); /* JG */
  124. static void edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points,
  125.                 int i, double *ex, double *ey)); /* HOE */
  126. static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));                /* JG */
  127. static TBOOLEAN two_edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
  128.  
  129. static double LogScale __PROTO((double coord, int is_log, double log_base_log,
  130.                 char *what, char *axis));
  131. static double dbl_raise __PROTO((double x, int y));
  132. static void boundary __PROTO((int scaling, struct curve_points * plots,
  133.                 int count));
  134. static double make_tics __PROTO((int axis, int guide));
  135.  
  136. /* widest2d_callback keeps longest so far in here */
  137. static int widest_tic;
  138.  
  139. static void widest2d_callback __PROTO((int axis, double place, char *text,
  140.                     struct lp_style_type grid));
  141. static void ytick2d_callback __PROTO((int axis, double place, char *text,
  142.                     struct lp_style_type grid));
  143. static void xtick2d_callback __PROTO((int axis, double place, char *text,
  144.                     struct lp_style_type grid));
  145. static void map_position __PROTO((struct position * pos, unsigned int *x,
  146.                     unsigned int *y, char *what));
  147. static void mant_exp __PROTO((double log_base, double x, int scientific,
  148.                 double *m, int *p));
  149. static void gprintf __PROTO((char *dest, size_t count, char *format,
  150.                 double log_base, double x));
  151.  
  152. #if defined(sun386) || defined(AMIGA_SC_6_1)
  153. static double CheckLog __PROTO((TBOOLEAN is_log, double base_log, double x));
  154. #endif
  155.  
  156. /* for plotting error bars
  157.  * half the width of error bar tic mark
  158.  */
  159. #define ERRORBARTIC (t->h_tic/2)
  160.  
  161. /*
  162.  * The Amiga SAS/C 6.2 compiler moans about macro envocations causing
  163.  * multiple calls to functions. I converted these macros to inline
  164.  * functions coping with the problem without loosing speed.
  165.  * If your compiler supports __inline, you should add it to the
  166.  * #ifdef directive
  167.  * (MGR, 1993)
  168.  */
  169.  
  170. #ifdef AMIGA_SC_6_1
  171. GP_INLINE static TBOOLEAN i_inrange(int z, int min, int max)
  172. {
  173.     return ((min < max) ? ((z >= min) && (z <= max)) : ((z >= max) && (z <= min)));
  174. }
  175.  
  176. GP_INLINE static double f_max(double a, double b)
  177. {
  178.     return (GPMAX(a, b));
  179. }
  180.  
  181. GP_INLINE static double f_min(double a, double b)
  182. {
  183.     return (GPMIN(a, b));
  184. }
  185.  
  186. #else
  187. #define f_max(a,b) GPMAX((a),(b))
  188. #define f_min(a,b) GPMIN((a),(b))
  189. #define i_inrange(z,a,b) inrange((z),(a),(b))
  190. #endif
  191.  
  192. /* True if a and b have the same sign or zero (positive or negative) */
  193. #define samesign(a,b) ((a) * (b) >= 0)
  194. /*}}} */
  195.  
  196. /*{{{  more variables */
  197. /* Define the boundary of the plot
  198.  * These are computed at each call to do_plot, and are constant over
  199.  * the period of one do_plot. They actually only change when the term
  200.  * type changes and when the 'set size' factors change.
  201.  * - no longer true, for 'set key out' or 'set key under'. also depend
  202.  * on tic marks and multi-line labels.
  203.  * They are shared with graph3d.c since we want to use its draw_clip_line()
  204.  */
  205. int xleft, xright, ybot, ytop;
  206.  
  207.  
  208. /* we make a local copy of the 'key' variable so that if something
  209.  * goes wrong, we can switch it off temporarily
  210.  */
  211.  
  212. static int lkey;
  213.  
  214. /* First attempt at double axes...
  215.  * x_min etc are now accessed from a global array min_array[], max_array[]
  216.  * put the scale factors into a similar array
  217.  * for convenience in this first attack on double axes, just define x_min etc
  218.  * since code already uses x_min, etc  Eventually it will be done properly
  219.  */
  220.  
  221.  
  222. extern double min_array[], max_array[];
  223. extern int auto_array[];
  224.  
  225. extern int log_array[];
  226. extern double base_array[], log_base_array[];
  227.  
  228. static int x_axis = FIRST_X_AXIS, y_axis = FIRST_Y_AXIS;    /* current axes */
  229.  
  230. static double scale[AXIS_ARRAY_SIZE];    /* scale factors for mapping for each axis */
  231.  
  232. /* BODGES BEFORE I FIX IT UP */
  233. #define x_min min_array[x_axis]
  234. #define x_max max_array[x_axis]
  235. #define y_min min_array[y_axis]
  236. #define y_max max_array[y_axis]
  237.  
  238. /* And the functions to map from user to terminal coordinates */
  239. /* maps floating point x to screen */
  240. #define map_x(x) (int)(xleft+(x-min_array[x_axis])*scale[x_axis]+0.5)
  241. /* same for y */
  242. #define map_y(y) (int)(ybot +(y-min_array[y_axis])*scale[y_axis]+0.5)
  243.  
  244. /* (DFK) Watch for cancellation error near zero on axes labels */
  245. /* less than one hundredth of a tic mark */
  246. #define SIGNIF (0.01)
  247. #define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
  248. #define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
  249. /*}}} */
  250.  
  251. /*{{{  CheckLog() */
  252. /* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog 
  253.  * macro, so I write it as a function on that machine.
  254.  *
  255.  * Amiga SAS/C 6.2 thinks it will do too much work calling functions in
  256.  * macro arguments twice, thus I inline theese functions. (MGR, 1993)
  257.  * If your compiler doesn't handle those macros correctly, you should
  258.  * also subscribe here. Even without inlining you gain speed with log plots
  259.  */
  260. #if defined(sun386) || defined(AMIGA_SC_6_1)
  261. GP_INLINE static double CheckLog(is_log, base_log, x)
  262. TBOOLEAN is_log;
  263. double base_log;
  264. double x;
  265. {
  266.     if (is_log)
  267.     return (pow(base_log, x));
  268.     else
  269.     return (x);
  270. }
  271. #else
  272. /* (DFK) Use 10^x if logscale is in effect, else x */
  273. #define CheckLog(is_log, base_log, x) ((is_log) ? pow(base_log, (x)) : (x))
  274. #endif /* sun386 || SAS/C */
  275. /*}}} */
  276.  
  277. /*{{{  LogScale() */
  278. static double LogScale(coord, is_log, log_base_log, what, axis)
  279. double coord;            /* the value */
  280. TBOOLEAN is_log;        /* is this axis in logscale? */
  281. double log_base_log;        /* if so, the log of its base */
  282. char *what;            /* what is the coord for? */
  283. char *axis;            /* which axis is this for ("x" or "y")? */
  284. {
  285.     if (is_log) {
  286.     if (coord <= 0.0) {
  287.         char errbuf[100];    /* place to write error message */
  288.         (void) sprintf(errbuf, "%s has %s coord of %g; must be above 0 for log scale!",
  289.                what, axis, coord);
  290.         graph_error(errbuf);
  291.     } else
  292.         return (log(coord) / log_base_log);
  293.     }
  294.     return (coord);
  295. }
  296. /*}}} */
  297.  
  298. /*{{{  graph_error() */
  299. /* handle errors during graph-plot in a consistent way */
  300. void graph_error(text)
  301. char *text;
  302. {
  303.     multiplot = FALSE;
  304.     term_end_plot();
  305.     int_error(text, NO_CARET);
  306. }
  307.  
  308. /*}}} */
  309.  
  310.  
  311. /*{{{  fixup_range() */
  312. /*
  313.  * === SYNOPSIS ===
  314.  *
  315.  * This function checks whether the data and/or plot range in a given axis
  316.  * is too small (which would cause divide-by-zero and/or infinite-loop
  317.  * problems later on).  If so,
  318.  * - if autoscaling is in effect for this axis, we widen the range
  319.  * - otherwise, we abort with a call to  int_error()  (which prints out
  320.  *   a suitable error message, then (hopefully) aborts this command and
  321.  *   returns to the command prompt or whatever).
  322.  *
  323.  *
  324.  * === HISTORY AND DESIGN NOTES ===
  325.  *
  326.  * 1998 Oct 4, Jonathan Thornburg <jthorn@galileo.thp.univie.ac.at>
  327.  *
  328.  * This function used to be a (long) macro  FIXUP_RANGE(AXIS, WHICH)
  329.  * which was (identically!) defined in  plot2d.c  and  plot3d.c .  As
  330.  * well as now being a function instead of a macro, the logic is also
  331.  * changed:  The "too small" range test no longer depends on 'set zero'
  332.  * and is now properly scaled relative to the data magnitude.
  333.  *
  334.  * The key question in designing this function is the policy for just how
  335.  * much to widen the data range by, as a function of the data magnitude.
  336.  * This is to some extent a matter of taste.  IMHO the key criterion is
  337.  * that (at least) all of the following should (a) not infinite-loop, and
  338.  * (b) give correct plots, regardless of the 'set zero' setting:
  339.  *      plot 6.02e23            # a huge number >> 1 / FP roundoff level
  340.  *      plot 3                  # a "reasonable-sized" number
  341.  *      plot 1.23e-12           # a small number still > FP roundoff level
  342.  *      plot 1.23e-12 * sin(x)  # a small function still > FP roundoff level
  343.  *      plot 1.23e-45           # a tiny number << FP roundoff level
  344.  *      plot 1.23e-45 * sin(x)  # a tiny function << FP roundoff level
  345.  *      plot 0          # or (more commonly) a data file of all zeros
  346.  * That is, IMHO gnuplot should *never* infinite-loop, and it should *never*
  347.  * producing an incorrect or misleading plot.  In contrast, the old code
  348.  * would infinite-loop on most of these examples with 'set zero 0.0' in
  349.  * effect, or would plot the small-amplitude sine waves as the zero function
  350.  * with 'zero' set larger than the sine waves' amplitude.
  351.  *
  352.  * The current code plots all the above examples correctly and without
  353.  * infinite looping.
  354.  *
  355.  *
  356.  * === USAGE ===
  357.  *
  358.  * Arguments:
  359.  * axis = (in) An integer specifying which axis (x1, x2, y1, y2, z, etc)
  360.  *             we should do our stuff for.  We use this argument as an
  361.  *             index into the global arrays  {min,max,auto}_array .  In
  362.  *             practice this argument will typically be one of the constants
  363.  *              {FIRST,SECOND}_{X,Y,Z}_AXIS  defined in plot.h.
  364.  * axis_name --> (in) This argument should point to the character string
  365.  *                    name corresponding to  axis , e.g. "x", "y2", etc.
  366.  *                    We use this (only) in formatting warning/error messages.
  367.  *
  368.  * Global Variables:
  369.  * auto_array[axis] = (in) (defined in command.c) Bit-flags which tell
  370.  *                         [in some manner I don't fully understand :=( ]
  371.  *                         whether and/or how autoscaling is in effect for
  372.  *                         this axis.
  373.  * {min,max}_array = (in out) (defined in command.c) The data ranges which
  374.  *                            this function manipulates.
  375.  * c_token = (in) (defined in plot.h) Used in formatting an error message.
  376.  *
  377.  * Bugs:
  378.  * - If  strlen(axis_name) > strlen("%s") , we may overflow an
  379.  *   error-message buffer, which would be A Bad Thing.  Caveat caller...
  380.  */
  381. void fixup_range(axis, axis_name)
  382. int axis;
  383. char *axis_name;
  384. {
  385. #define MAX_AXIS_NAME_LEN    2    /* max legal strlen(axis_name) */
  386.  
  387. /* These two macro definitions set the range-widening policy: */
  388. #define FIXUP_RANGE__WIDEN_ZERO_ABS    1.0    /* widen [0:0] by
  389.                            +/- this absolute amount */
  390. #define FIXUP_RANGE__WIDEN_NONZERO_REL    0.01    /* widen [nonzero:nonzero] by
  391.                            -/+ this relative amount */
  392.  
  393.     double dmin = min_array[axis];
  394.     double dmax = max_array[axis];
  395.     if (dmax - dmin == 0.0) {
  396.     /* empty range */
  397.     if (auto_array[axis]) {
  398.         /* range came from autoscaling ==> widen it */
  399.         double widen = (dmax == 0.0)
  400.         ? FIXUP_RANGE__WIDEN_ZERO_ABS
  401.         : FIXUP_RANGE__WIDEN_NONZERO_REL * dmax;
  402.         fprintf(stderr,
  403.             "Warning: empty %s range [%g:%g], ",
  404.             axis_name, dmin, dmax);
  405.         min_array[axis] -= widen;
  406.         max_array[axis] += widen;
  407.         fprintf(stderr,
  408.             "adjusting to [%g:%g]\n",
  409.             min_array[axis], max_array[axis]);
  410.     } else {
  411.         /* user has explicitly set the range */
  412.         /* (to something empty) ==> we're in trouble */
  413.         char msg_buffer[MAX_LINE_LEN + 1];
  414.         sprintf(msg_buffer, "Can't plot with an empty %s range!", axis_name);
  415.         int_error(msg_buffer, c_token);    /* never returns */
  416.     }
  417.     }
  418. }
  419.  
  420. /*}}} */
  421.  
  422.  
  423. /*{{{  widest2d_callback() */
  424. /* we determine widest tick label by getting gen_ticks to call this
  425.  * routine with every label
  426.  */
  427.  
  428. static void widest2d_callback(axis, place, text, grid)
  429. int axis;
  430. double place;
  431. char *text;
  432. struct lp_style_type grid;
  433. {
  434.     int len = label_width(text, NULL);
  435.     if (len > widest_tic)
  436.     widest_tic = len;
  437. }
  438. /*}}} */
  439.  
  440.  
  441. /*{{{  boundary() */
  442. /* borders of plotting area
  443.  * computed once on every call to do_plot
  444.  *
  445.  * The order in which things is done is getting pretty critical:
  446.  *  ytop depends on title, x2label, ylabels (if no rotated text)
  447.  *  ybot depends on key, if TUNDER
  448.  *  once we have these, we can setup the y1 and y2 tics and the 
  449.  *  only then can we calculate xleft and xright
  450.  *  xright depends also on key TRIGHT
  451.  *  then we can do x and x2 tics
  452.  *
  453.  * For set size ratio ..., everything depends on everything else...
  454.  * not really a lot we can do about that, so we lose if the plot has to
  455.  * be reduced vertically. But the chances are the
  456.  * change will not be very big, so the number of tics will not
  457.  * change dramatically.
  458.  *
  459.  * Margin computation redone by Dick Crawford (rccrawford@lanl.gov) 4/98
  460.  */
  461.  
  462. static void boundary(scaling, plots, count)
  463. TBOOLEAN scaling;        /* TRUE if terminal is doing the scaling */
  464. struct curve_points *plots;
  465. int count;
  466. {
  467.     int ytlen;
  468.     int yticlin = 0, y2ticlin = 0, timelin = 0;
  469.  
  470.     register struct termentry *t = term;
  471.     int key_h, key_w;
  472.     int can_rotate = (*t->text_angle) (1);
  473.  
  474.     int xtic_textheight;    /* height of xtic labels */
  475.     int x2tic_textheight;    /* height of x2tic labels */
  476.     int title_textheight;    /* height of title */
  477.     int xlabel_textheight;    /* height of xlabel */
  478.     int x2label_textheight;    /* height of x2label */
  479.     int timetop_textheight;    /* height of timestamp (if at top) */
  480.     int timebot_textheight;    /* height of timestamp (if at bottom) */
  481.     int ylabel_textheight;    /* height of (unrotated) ylabel */
  482.     int y2label_textheight;    /* height of (unrotated) y2label */
  483.     int ylabel_textwidth;    /* width of (rotated) ylabel */
  484.     int y2label_textwidth;    /* width of (rotated) y2label */
  485.     int timelabel_textwidth;    /* width of timestamp */
  486.     int ytic_textwidth;        /* width of ytic labels */
  487.     int y2tic_textwidth;    /* width of y2tic labels */
  488.     int x2tic_height;        /* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
  489.     int xtic_height;
  490.     int ytic_width;
  491.     int y2tic_width;
  492.  
  493.     /* figure out which rotatable items are to be rotated
  494.      * (ylabel and y2label are rotated if possible) */
  495.     int vertical_timelabel = can_rotate && timelabel_rotate;
  496.     int vertical_xtics = can_rotate && rotate_xtics;
  497.     int vertical_x2tics = can_rotate && rotate_x2tics;
  498.     int vertical_ytics = can_rotate && rotate_ytics;
  499.     int vertical_y2tics = can_rotate && rotate_y2tics;
  500.  
  501.     lkey = key;            /* but we may have to disable it later */
  502.  
  503.     xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = 0;
  504.  
  505.     /*{{{  count lines in labels and tics */
  506.     if (*title.text)
  507.     label_width(title.text, &titlelin);
  508.     if (*xlabel.text)
  509.     label_width(xlabel.text, &xlablin);
  510.     if (*x2label.text)
  511.     label_width(x2label.text, &x2lablin);
  512.     if (*ylabel.text)
  513.     label_width(ylabel.text, &ylablin);
  514.     if (*y2label.text)
  515.     label_width(y2label.text, &y2lablin);
  516.     if (xtics)
  517.     label_width(xformat, &xticlin);
  518.     if (x2tics)
  519.     label_width(x2format, &x2ticlin);
  520.     if (ytics)
  521.     label_width(yformat, &yticlin);
  522.     if (y2tics)
  523.     label_width(y2format, &y2ticlin);
  524.     if (*timelabel.text)
  525.     label_width(timelabel.text, &timelin);
  526.     /*}}} */
  527.  
  528.     /*{{{  preliminary ytop  calculation */
  529.  
  530.     /*     first compute heights of things to be written in the margin */
  531.  
  532.     /* title */
  533.     if (titlelin)
  534.     title_textheight = (int) ((titlelin + title.yoffset + 1) * (t->v_char));
  535.     else
  536.     title_textheight = 0;
  537.  
  538.     /* x2label */
  539.     if (x2lablin) {
  540.     x2label_textheight = (int) ((x2lablin + x2label.yoffset) * (t->v_char));
  541.     if (!x2tics)
  542.         x2label_textheight += 0.5 * t->v_char;
  543.     } else
  544.     x2label_textheight = 0;
  545.  
  546.     /* tic labels */
  547.     if (x2tics & TICS_ON_BORDER) {
  548.     /* ought to consider tics on axes if axis near border */
  549.     if (vertical_x2tics) {
  550.         /* guess at tic length, since we don't know it yet
  551.            --- we'll fix it after the tic labels have been created */
  552.         x2tic_textheight = (int) (5 * (t->h_char));
  553.     } else
  554.         x2tic_textheight = (int) ((x2ticlin) * (t->v_char));
  555.     } else
  556.     x2tic_textheight = 0;
  557.  
  558.     /* tics */
  559.     if (!tic_in && ((x2tics & TICS_ON_BORDER) || ((xtics & TICS_MIRROR) && (xtics & TICS_ON_BORDER))))
  560.     x2tic_height = (int) ((t->v_tic) * ticscale);
  561.     else
  562.     x2tic_height = 0;
  563.  
  564.     /* timestamp */
  565.     if (*timelabel.text && !timelabel_bottom)
  566.     timetop_textheight = (int) ((timelin + timelabel.yoffset + 2) * (t->v_char));
  567.     else
  568.     timetop_textheight = 0;
  569.  
  570.     /* horizontal ylabel */
  571.     if (*ylabel.text && !can_rotate)
  572.     ylabel_textheight = (int) ((ylablin + ylabel.yoffset) * (t->v_char));
  573.     else
  574.     ylabel_textheight = 0;
  575.  
  576.     /* horizontal y2label */
  577.     if (*y2label.text && !can_rotate)
  578.     y2label_textheight = (int) ((y2lablin + y2label.yoffset) * (t->v_char));
  579.     else
  580.     y2label_textheight = 0;
  581.  
  582.     /* compute ytop from the various components
  583.      *     unless tmargin is explicitly specified  */
  584.  
  585.     ytop = (int) ((ysize + yoffset) * (t->ymax));
  586.  
  587.     if (tmargin < 0) {
  588.     int top_margin = x2label_textheight + title_textheight;
  589.     if (timetop_textheight + ylabel_textheight > top_margin)
  590.         top_margin = timetop_textheight + ylabel_textheight;
  591.     if (y2label_textheight > top_margin)
  592.         top_margin = y2label_textheight;
  593.  
  594.     top_margin += x2tic_height + x2tic_textheight;
  595.     /* x2tic_height and x2tic_textheight are computed as only the
  596.      *     relevant heights, but they nonetheless need a blank
  597.      *     space above them  */
  598.     if (top_margin > x2tic_height)
  599.         top_margin += (int) (t->v_char);
  600.  
  601.     ytop -= top_margin;
  602.     if (ytop == (int) (0.5 + (ysize + yoffset) * (t->ymax))) {
  603.         /* make room for the end of rotated ytics or y2tics */
  604.         ytop -= (int) ((t->h_char) * 2);
  605.     }
  606.     } else
  607.     ytop -= (int) ((t->v_char) * tmargin);
  608.  
  609.     /*  end of preliminary ytop calculation }}} */
  610.  
  611.  
  612.     /*{{{  tentative xleft, needed for TUNDER */
  613.     if (lmargin >= 0)
  614.     xleft = (int) (xoffset * (t->xmax) + (t->h_char) * lmargin);
  615.     else
  616.     xleft = (int) (xoffset * (t->xmax) + (t->h_char) * 2);
  617.     /*}}} */
  618.  
  619.  
  620.     /*{{{  tentative xright, needed for TUNDER */
  621.     if (rmargin >= 0)
  622.     xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * rmargin);
  623.     else
  624.     xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * 2);
  625.     /*}}} */
  626.  
  627.  
  628.     /*{{{  preliminary ybot calculation
  629.      *     first compute heights of labels and tics */
  630.  
  631.     /* tic labels */
  632.     if (xtics & TICS_ON_BORDER) {
  633.     /* ought to consider tics on axes if axis near border */
  634.     if (vertical_xtics) {
  635.         /* guess at tic length, since we don't know it yet */
  636.         xtic_textheight = (int) ((t->h_char) * 5);
  637.     } else
  638.         xtic_textheight = (int) ((t->v_char) * (xticlin + 1));
  639.     } else
  640.     xtic_textheight = 0;
  641.  
  642.     /* tics */
  643.     if (!tic_in && ((xtics & TICS_ON_BORDER) || ((x2tics & TICS_MIRROR) && (x2tics & TICS_ON_BORDER))))
  644.     xtic_height = (int) ((t->v_tic) * ticscale);
  645.     else
  646.     xtic_height = 0;
  647.  
  648.     /* xlabel */
  649.     if (xlablin) {
  650.     /* offset is subtracted because if > 0, the margin is smaller */
  651.     xlabel_textheight = (int) ((xlablin - xlabel.yoffset) * (t->v_char));
  652.     if (!xtics)
  653.         xlabel_textheight += 0.5 * t->v_char;
  654.     } else
  655.     xlabel_textheight = 0;
  656.  
  657.     /* timestamp */
  658.     if (*timelabel.text && timelabel_bottom) {
  659.     /* && !vertical_timelabel)
  660.      * DBT 11-18-98 resize plot for vertical timelabels too !
  661.      */
  662.     /* offset is subtracted because if . 0, the margin is smaller */
  663.     timebot_textheight = (int) ((timelin - timelabel.yoffset) * (t->v_char));
  664.     } else
  665.     timebot_textheight = 0;
  666.  
  667.     /* compute ybot from the various components
  668.      *     unless bmargin is explicitly specified  */
  669.  
  670.     ybot = (int) ((t->ymax) * yoffset);
  671.  
  672.     if (bmargin < 0) {
  673.     ybot += xtic_height + xtic_textheight;
  674.     if (xlabel_textheight > 0)
  675.         ybot += xlabel_textheight;
  676.     if (timebot_textheight > 0)
  677.         ybot += timebot_textheight;
  678.     /* HBB 19990616: round to nearest integer, required to escape
  679.      * floating point inaccuracies */
  680.     if (ybot == (int)(0.5 + (t->ymax) * yoffset)) {
  681.         /* make room for the end of rotated ytics or y2tics */
  682.         ybot += (int) ((t->h_char) * 2);
  683.     }
  684.     } else
  685.     ybot += (int) (bmargin * (t->v_char));
  686.  
  687.     /*  end of preliminary ybot calculation }}} */
  688.  
  689.  
  690. #define KEY_PANIC(x) if (x) { lkey = 0; goto key_escape; }
  691.  
  692.     if (lkey) {
  693.     /*{{{  essential key features */
  694.     p_width = pointsize * t->h_tic;
  695.     p_height = pointsize * t->v_tic;
  696.  
  697.     if (key_swidth >= 0)
  698.         key_sample_width = key_swidth * (t->h_char) + p_width;
  699.     else
  700.         key_sample_width = 0;
  701.  
  702.     key_entry_height = p_height * 1.25 * key_vert_factor;
  703.     if (key_entry_height < (t->v_char))
  704.         key_entry_height = (t->v_char) * key_vert_factor;
  705.  
  706.     /* count max_len key and number keys with len > 0 */
  707.     max_ptitl_len = find_maxl_keys(plots, count, &ptitl_cnt);
  708.     if ((ytlen = label_width(key_title, &ktitl_lines)) > max_ptitl_len)
  709.         max_ptitl_len = ytlen;
  710.  
  711.     if (key_reverse) {
  712.         key_sample_left = -key_sample_width;
  713.         key_sample_right = 0;
  714.         /* if key width is being used, adjust right-justified text */
  715.         key_text_left = t->h_char;
  716.         key_text_right = (t->h_char) * (max_ptitl_len + 1 + key_width_fix);
  717.         key_size_left = t->h_char - key_sample_left;    /* sample left is -ve */
  718.         key_size_right = key_text_right;
  719.     } else {
  720.         key_sample_left = 0;
  721.         key_sample_right = key_sample_width;
  722.         /* if key width is being used, adjust left-justified text */
  723.         key_text_left = -(int) ((t->h_char) * (max_ptitl_len + 1 + key_width_fix));
  724.         key_text_right = -(int) (t->h_char);
  725.         key_size_left = -key_text_left;
  726.         key_size_right = key_sample_right + t->h_char;
  727.     }
  728.     key_point_offset = (key_sample_left + key_sample_right) / 2;
  729.  
  730.     /* advance width for cols */
  731.     key_col_wth = key_size_left + key_size_right;
  732.  
  733.     key_rows = ptitl_cnt;
  734.     key_cols = 1;
  735.  
  736.     /* calculate rows and cols for key - if something goes wrong,
  737.      * the tidiest way out is to  set lkey = 0, and a goto
  738.      */
  739.  
  740.     if (lkey == -1) {
  741.         if (key_vpos == TUNDER) {
  742.         /* maximise no cols, limited by label-length */
  743.         key_cols = (int) (xright - xleft) / key_col_wth;
  744.         KEY_PANIC(key_cols == 0);
  745.         key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
  746.         KEY_PANIC(key_rows == 0);
  747.         /* now calculate actual no cols depending on no rows */
  748.         key_cols = (int) (ptitl_cnt + key_rows - 1) / key_rows;
  749.         KEY_PANIC(key_cols == 0);
  750.         key_col_wth = (int) (xright - xleft) / key_cols;
  751.         /* we divide into columns, then centre in column by considering
  752.          * ratio of * key_left_size to key_right_size
  753.          *
  754.          * key_size_left/(key_size_left+key_size_right) * (xright-xleft)/key_cols
  755.          * do one integer division to maximise accuracy (hope we
  756.          * don't overflow !)
  757.          */
  758.         key_xl = xleft - key_size_left + ((xright - xleft) * key_size_left) / (key_cols * (key_size_left + key_size_right));
  759.         key_xr = key_xl + key_col_wth * (key_cols - 1) + key_size_left + key_size_right;
  760.         key_yb = t->ymax * yoffset;
  761.         key_yt = key_yb + key_rows * key_entry_height + ktitl_lines * t->v_char;
  762.         ybot += key_entry_height * key_rows + (int) ((t->v_char) * (ktitl_lines + 1));
  763.         } else {
  764.         /* maximise no rows, limited by ytop-ybot */
  765.         int i = (int) (ytop - ybot - (ktitl_lines + 1) * (t->v_char)) / key_entry_height;
  766.         KEY_PANIC(i == 0);
  767.         if (ptitl_cnt > i) {
  768.             key_cols = (int) (ptitl_cnt + i - 1) / i;
  769.             /* now calculate actual no rows depending on no cols */
  770.             KEY_PANIC(key_cols == 0);
  771.             key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
  772.         }
  773.         }
  774.         /* come here if we detect a division by zero in key calculations */
  775.       key_escape:
  776.         ;            /* ansi requires this */
  777.     }
  778.     /*}}} */
  779.     }
  780.     /*{{{  set up y and y2 tics */
  781.     {
  782.     /* setup_tics allows max number of tics to be specified
  783.      * but users dont like it to change with size and font,
  784.      * so we use value of 20, which is 3.5 behaviour.
  785.      * Note also that if format is '', yticlin = 0, so this gives
  786.      * division by zero. 
  787.      * int guide = (ytop-ybot)/term->v_char;
  788.      */
  789.     if (ytics)
  790.         setup_tics(FIRST_Y_AXIS, &yticdef, yformat, 20 /*(int) (guide/yticlin) */ );
  791.     if (y2tics)
  792.         setup_tics(SECOND_Y_AXIS, &y2ticdef, y2format, 20 /*(int) (guide/y2ticlin) */ );
  793.     }
  794.     /*}}} */
  795.  
  796.  
  797.     /*{{{  recompute xleft based on widths of ytics, ylabel etc
  798.        unless it has been explicitly set by lmargin */
  799.  
  800.     /* tic labels */
  801.     if (ytics & TICS_ON_BORDER) {
  802.     if (vertical_ytics)
  803.         /* HBB: we will later add some white space as part of this, so
  804.          * reserve two more rows (one above, one below the text ...).
  805.          * Same will be done to similar calc.'s elsewhere */
  806.         ytic_textwidth = (int) ((t->v_char) * (yticlin + 2));
  807.     else {
  808.         widest_tic = 0;    /* reset the global variable ... */
  809.         /* get gen_tics to call widest2d_callback with all labels
  810.          * the latter sets widest_tic to the length of the widest one
  811.          * ought to consider tics on axis if axis near border...
  812.          */
  813.         gen_tics(FIRST_Y_AXIS, &yticdef, 0, 0, 0.0, widest2d_callback);
  814.  
  815.         ytic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
  816.     }
  817.     } else {
  818.     ytic_textwidth = 0;
  819.     }
  820.  
  821.     /* tics */
  822.     if (!tic_in && ((ytics & TICS_ON_BORDER) || ((y2tics & TICS_MIRROR) && (y2tics & TICS_ON_BORDER))))
  823.     ytic_width = (int) ((t->h_tic) * ticscale);
  824.     else
  825.     ytic_width = 0;
  826.  
  827.     /* ylabel */
  828.     if (*ylabel.text && can_rotate) {
  829.     ylabel_textwidth = (int) ((ylablin - ylabel.xoffset) * (t->v_char));
  830.     if (!ytics)
  831.         ylabel_textwidth += 0.5 * t->v_char;
  832.     }
  833.     /* this should get large for NEGATIVE ylabel.xoffsets  DBT 11-5-98 */
  834.     else
  835.     ylabel_textwidth = 0;
  836.  
  837.     /* timestamp */
  838.     if (*timelabel.text && vertical_timelabel)
  839.     timelabel_textwidth = (int) ((timelin - timelabel.xoffset + 1.5) * (t->v_char));
  840.     else
  841.     timelabel_textwidth = 0;
  842.  
  843.     /* compute xleft from the various components
  844.      *     unless lmargin is explicitly specified  */
  845.  
  846.     xleft = (int) ((t->xmax) * xoffset);
  847.  
  848.     if (lmargin < 0) {
  849.     xleft += (timelabel_textwidth > ylabel_textwidth ? timelabel_textwidth : ylabel_textwidth)
  850.         + ytic_width + ytic_textwidth;
  851.  
  852.     /* make sure xleft is wide enough for a negatively
  853.      * x-offset horizontal timestamp
  854.      */
  855.     if (!vertical_timelabel && xleft - ytic_width - ytic_textwidth < -(int) (timelabel.xoffset * (t->h_char)))
  856.     xleft = ytic_width + ytic_textwidth - (int) (timelabel.xoffset * (t->h_char));
  857.     if (xleft == (int)(0.5 + (t->xmax) * xoffset)) {
  858.         /* make room for end of xtic or x2tic label */
  859.         xleft += (int) ((t->h_char) * 2);
  860.     }
  861.     /* DBT 12-3-98  extra margin just in case */
  862.     xleft += 0.5 * t->v_char;
  863.     } else
  864.     xleft += (int) (lmargin * (t->h_char));
  865.  
  866.     /*  end of xleft calculation }}} */
  867.  
  868.  
  869.     /*{{{  recompute xright based on widest y2tic. y2labels, key TOUT
  870.        unless it has been explicitly set by rmargin */
  871.  
  872.     /* tic labels */
  873.     if (y2tics & TICS_ON_BORDER) {
  874.     if (vertical_y2tics)
  875.         y2tic_textwidth = (int) ((t->v_char) * (y2ticlin + 2));
  876.     else {
  877.         widest_tic = 0;    /* reset the global variable ... */
  878.         /* get gen_tics to call widest2d_callback with all labels
  879.          * the latter sets widest_tic to the length of the widest one
  880.          * ought to consider tics on axis if axis near border...
  881.          */
  882.         gen_tics(SECOND_Y_AXIS, &y2ticdef, 0, 0, 0.0, widest2d_callback);
  883.  
  884.         y2tic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
  885.     }
  886.     } else {
  887.     y2tic_textwidth = 0;
  888.     }
  889.  
  890.     /* tics */
  891.     if (!tic_in && ((y2tics & TICS_ON_BORDER) || ((ytics & TICS_MIRROR) && (ytics & TICS_ON_BORDER))))
  892.     y2tic_width = (int) ((t->h_tic) * ticscale);
  893.     else
  894.     y2tic_width = 0;
  895.  
  896.     /* y2label */
  897.     if (can_rotate && *y2label.text) {
  898.     y2label_textwidth = (int) ((y2lablin + y2label.xoffset) * (t->v_char));
  899.     if (!y2tics)
  900.         y2label_textwidth += 0.5 * t->v_char;
  901.     } else
  902.     y2label_textwidth = 0;
  903.  
  904.     /* compute xright from the various components
  905.      *     unless rmargin is explicitly specified  */
  906.  
  907.     xright = (int) ((t->xmax) * (xsize + xoffset));
  908.  
  909.     if (rmargin < 0) {
  910.     /* xright -= y2label_textwidth + y2tic_width + y2tic_textwidth; */
  911.     xright -= y2tic_width + y2tic_textwidth;
  912.     if (y2label_textwidth > 0)
  913.         xright -= y2label_textwidth;
  914.  
  915.     /* adjust for outside key */
  916.     if (lkey == -1 && key_hpos == TOUT) {
  917.         xright -= key_col_wth * key_cols;
  918.         key_xl = xright + (int) (t->h_tic);
  919.     }
  920.     if (xright == (int)(0.5 + (t->xmax) * (xsize + xoffset))) {
  921.         /* make room for end of xtic or x2tic label */
  922.         xright -= (int) ((t->h_char) * 2);
  923.     }
  924.     xright -= 0.5 * t->v_char;    /* DBT 12-3-98  extra margin just in case */
  925.  
  926.     } else
  927.     xright -= (int) (rmargin * (t->h_char));
  928.  
  929.     /*  end of xright calculation }}} */
  930.  
  931.  
  932.     if (aspect_ratio != 0.0) {
  933.     double current_aspect_ratio;
  934.  
  935.     if (aspect_ratio < 0 && (max_array[x_axis] - min_array[x_axis]) != 0.0) {
  936.         current_aspect_ratio = -aspect_ratio * fabs((max_array[y_axis] - min_array[y_axis]) / (max_array[x_axis] - min_array[x_axis]));
  937.     } else
  938.         current_aspect_ratio = aspect_ratio;
  939.  
  940.     /*{{{  set aspect ratio if valid and sensible */
  941.     if (current_aspect_ratio >= 0.01 && current_aspect_ratio <= 100.0) {
  942.         double current = ((double) (ytop - ybot)) / ((double) (xright - xleft));
  943.         double required = (current_aspect_ratio * (double) t->v_tic) / ((double) t->h_tic);
  944.  
  945.         if (current > required) {
  946.         /* too tall */
  947.         ytop = ybot + required * (xright - xleft);
  948.         } else {
  949.         xright = xleft + (ytop - ybot) / required;
  950.         }
  951.     }
  952.     /*}}} */
  953.     }
  954.     /*{{{  set up x and x2 tics */
  955.     /* we should base the guide on the width of the xtics, but we cannot
  956.      * use widest_tics until tics are set up. Bit of a downer - let us
  957.      * assume tics are 5 characters wide
  958.      */
  959.  
  960.     {
  961.     /* see equivalent code for ytics above
  962.      * int guide = (xright - xleft) / (5*t->h_char);
  963.      */
  964.  
  965.     if (xtics)
  966.         setup_tics(FIRST_X_AXIS, &xticdef, xformat, 20 /*guide */ );
  967.     if (x2tics)
  968.         setup_tics(SECOND_X_AXIS, &x2ticdef, x2format, 20 /*guide */ );
  969.     }
  970.     /*}}} */
  971.  
  972.  
  973.     /*  adjust top and bottom margins for tic label rotation */
  974.  
  975.     if (tmargin < 0 && x2tics & TICS_ON_BORDER && vertical_x2tics) {
  976.     widest_tic = 0;        /* reset the global variable ... */
  977.     gen_tics(SECOND_X_AXIS, &x2ticdef, 0, 0, 0.0, widest2d_callback);
  978.     ytop += x2tic_textheight;
  979.     /* Now compute a new one and use that instead: */
  980.     x2tic_textheight = (int) ((t->h_char) * (widest_tic));
  981.     ytop -= x2tic_textheight;
  982.     }
  983.     if (bmargin < 0 && xtics & TICS_ON_BORDER && vertical_xtics) {
  984.     widest_tic = 0;        /* reset the global variable ... */
  985.     gen_tics(FIRST_X_AXIS, &xticdef, 0, 0, 0.0, widest2d_callback);
  986.     ybot -= xtic_textheight;
  987.     xtic_textheight = (int) ((t->h_char) * widest_tic);
  988.     ybot += xtic_textheight;
  989.     }
  990.     /*  compute coordinates for axis labels, title et al
  991.      *     (some of these may not be used) */
  992.  
  993.     x2label_y = ytop + x2tic_height + x2tic_textheight + x2label_textheight;
  994.     if (x2tic_textheight && (title_textheight || x2label_textheight))
  995.     x2label_y += t->v_char;
  996.  
  997.     title_y = x2label_y + title_textheight;
  998.  
  999.     ylabel_y = ytop + x2tic_height + x2tic_textheight + ylabel_textheight;
  1000.  
  1001.     y2label_y = ytop + x2tic_height + x2tic_textheight + y2label_textheight;
  1002.  
  1003.     xlabel_y = ybot - xtic_height - xtic_textheight - xlabel_textheight + xlablin*(t->v_char);
  1004.     ylabel_x = xleft - ytic_width - ytic_textwidth;
  1005.     if (*ylabel.text && can_rotate)
  1006.     ylabel_x -= ylabel_textwidth;
  1007.  
  1008.     y2label_x = xright + y2tic_width + y2tic_textwidth;
  1009.     if (*y2label.text && can_rotate)
  1010.     y2label_x += y2label_textwidth - y2lablin * t->v_char;
  1011.  
  1012.     if (vertical_timelabel) {
  1013.     if (timelabel_bottom)
  1014.         time_y = xlabel_y - timebot_textheight + xlabel_textheight;
  1015.     else {
  1016.         time_y = title_y + timetop_textheight - title_textheight - x2label_textheight;
  1017.     }
  1018.     } else {
  1019.     if (timelabel_bottom)
  1020.         time_y = ybot - xtic_height - xtic_textheight - xlabel_textheight - timebot_textheight + t->v_char;
  1021.     else if (ylabel_textheight > 0)
  1022.         time_y = ylabel_y + timetop_textheight;
  1023.     else
  1024.         time_y = ytop + x2tic_height + x2tic_textheight + timetop_textheight + (int) (t->h_char);
  1025.     }
  1026.     if (vertical_timelabel)
  1027.     time_x = xleft - ytic_width - ytic_textwidth - timelabel_textwidth;
  1028.     else
  1029.     time_x = xleft - ytic_width - ytic_textwidth + (int) (timelabel.xoffset * (t->h_char));
  1030.  
  1031.     xtic_y = ybot - xtic_height - (vertical_xtics ? (int) (t->h_char) : (int) (t->v_char));
  1032.  
  1033.     x2tic_y = ytop + x2tic_height + (vertical_x2tics ? (int) (t->h_char) : x2tic_textheight);
  1034.  
  1035.     ytic_x = xleft - ytic_width - (vertical_ytics ? (ytic_textwidth - (int) t->v_char) : (int) (t->h_char));
  1036.  
  1037.     y2tic_x = xright + y2tic_width + (vertical_y2tics ? (int) (t->v_char) : (int) (t->h_char));
  1038.  
  1039.     /* restore text to horizontal [we tested rotation above] */
  1040.     (void) (*t->text_angle) (0);
  1041.  
  1042.     /* needed for map_position() below */
  1043.  
  1044.     scale[FIRST_Y_AXIS] = (ytop - ybot) / (max_array[FIRST_Y_AXIS] - min_array[FIRST_Y_AXIS]);
  1045.     scale[FIRST_X_AXIS] = (xright - xleft) / (max_array[FIRST_X_AXIS] - min_array[FIRST_X_AXIS]);
  1046.     scale[SECOND_Y_AXIS] = (ytop - ybot) / (max_array[SECOND_Y_AXIS] - min_array[SECOND_Y_AXIS]);
  1047.     scale[SECOND_X_AXIS] = (xright - xleft) / (max_array[SECOND_X_AXIS] - min_array[SECOND_X_AXIS]);
  1048.  
  1049.     /*{{{  calculate the window in the grid for the key */
  1050.     if (lkey == 1 || (lkey == -1 && key_vpos != TUNDER)) {
  1051.     /* calculate space for keys to prevent grid overwrite the keys */
  1052.     /* do it even if there is no grid, as do_plot will use these to position key */
  1053.     key_w = key_col_wth * key_cols;
  1054.     key_h = (ktitl_lines) * t->v_char + key_rows * key_entry_height;
  1055.     if (lkey == -1) {
  1056.         if (key_vpos == TTOP) {
  1057.         key_yt = (int) ytop - (t->v_tic);
  1058.         key_yb = key_yt - key_h;
  1059.         } else {
  1060.         key_yb = ybot + (t->v_tic);
  1061.         key_yt = key_yb + key_h;
  1062.         }
  1063.         if (key_hpos == TLEFT) {
  1064.         key_xl = xleft + (t->h_char);    /* for Left just */
  1065.         key_xr = key_xl + key_w;
  1066.         } else if (key_hpos == TRIGHT) {
  1067.         key_xr = xright - (t->h_char);    /* for Right just */
  1068.         key_xl = key_xr - key_w;
  1069.         } else {        /* TOUT */
  1070.         /* do this here for do_plot() */
  1071.         /* align right first since rmargin may be manual */
  1072.         key_xr = (xsize + xoffset) * (t->xmax) - (t->h_char);
  1073.         key_xl = key_xr - key_w;
  1074.         }
  1075.     } else {
  1076.         unsigned int x, y;
  1077.         map_position(&key_user_pos, &x, &y, "key");
  1078.         key_xl = x - key_size_left;
  1079.         key_xr = key_xl + key_w;
  1080.         key_yt = y + (ktitl_lines ? t->v_char : key_entry_height) / 2;
  1081.         key_yb = key_yt - key_h;
  1082.     }
  1083.     }
  1084.     /*}}} */
  1085.  
  1086. }
  1087.  
  1088. /*}}} */
  1089.  
  1090. /*{{{  dbl_raise() */
  1091. static double dbl_raise(x, y)
  1092. double x;
  1093. int y;
  1094. {
  1095.     register int i = abs(y);
  1096.     double val = 1.0;
  1097.  
  1098.     while (--i >= 0)
  1099.     val *= x;
  1100.  
  1101.     if (y < 0)
  1102.     return (1.0 / val);
  1103.     return (val);
  1104. }
  1105. /*}}} */
  1106.  
  1107. /*{{{  timetic_fmt() */
  1108. void timetic_format(axis, amin, amax)
  1109. int axis;
  1110. double amin, amax;
  1111. {
  1112.     struct tm tmin, tmax;
  1113.  
  1114.     *ticfmt[axis] = 0;        /* make sure we strcat to empty string */
  1115.  
  1116.     ggmtime(&tmin, (double) time_tic_just(timelevel[axis], amin));
  1117.     ggmtime(&tmax, (double) time_tic_just(timelevel[axis], amax));
  1118.     /*
  1119.        if ( tmax.tm_year == tmin.tm_year && 
  1120.        tmax.tm_mon == tmin.tm_mon && 
  1121.        tmax.tm_mday == tmin.tm_mday ) {
  1122.      */
  1123.     if (tmax.tm_year == tmin.tm_year &&
  1124.     tmax.tm_yday == tmin.tm_yday) {
  1125.     /* same day, skip date */
  1126.     if (tmax.tm_hour != tmin.tm_hour) {
  1127.         strcpy(ticfmt[axis], "%H");
  1128.     }
  1129.     if (timelevel[axis] < 3) {
  1130.         if (strlen(ticfmt[axis]))
  1131.         strcat(ticfmt[axis], ":");
  1132.         strcat(ticfmt[axis], "%M");
  1133.     }
  1134.     if (timelevel[axis] < 2) {
  1135.         strcat(ticfmt[axis], ":%S");
  1136.     }
  1137.     } else {
  1138.     if (tmax.tm_year != tmin.tm_year) {
  1139.         /* different years, include year in ticlabel */
  1140.         /* check convention, day/month or month/day */
  1141.         if (strchr(timefmt, 'm') < strchr(timefmt, 'd')) {
  1142.         strcpy(ticfmt[axis], "%m/%d/%");
  1143.         } else {
  1144.         strcpy(ticfmt[axis], "%d/%m/%");
  1145.         }
  1146.         if (((int) (tmax.tm_year / 100)) != ((int) (tmin.tm_year / 100))) {
  1147.         strcat(ticfmt[axis], "Y");
  1148.         } else {
  1149.         strcat(ticfmt[axis], "y");
  1150.         }
  1151.  
  1152.     } else {
  1153.         if (strchr(timefmt, 'm') < strchr(timefmt, 'd')) {
  1154.         strcpy(ticfmt[axis], "%m/%d");
  1155.         } else {
  1156.         strcpy(ticfmt[axis], "%d/%m");
  1157.         }
  1158.     }
  1159.     if (timelevel[axis] < 4) {
  1160.         strcat(ticfmt[axis], "\n%H:%M");
  1161.     }
  1162.     }
  1163. }
  1164. /*}}} */
  1165.  
  1166. /*{{{  set_tic() */
  1167. /* the guide parameter was intended to allow the number of tics
  1168.  * to depend on the relative sizes of the plot and the font.
  1169.  * It is the approximate upper limit on number of tics allowed.
  1170.  * But it did not go down well with the users.
  1171.  * A value of 20 gives the same behaviour as 3.5, so that is
  1172.  * hardwired into the calls to here. Maybe we will restore it
  1173.  * to the automatic calculation one day
  1174.  */
  1175.  
  1176. double set_tic(l10, guide)
  1177. double l10;
  1178. int guide;
  1179. {
  1180.     double xnorm, tics, posns;
  1181.  
  1182.     int fl = (int) floor(l10);
  1183.     xnorm = pow(10.0, l10 - fl);    /* approx number of decades */
  1184.  
  1185.     posns = guide / xnorm;    /* approx number of tic posns per decade */
  1186.  
  1187.     if (posns > 40)
  1188.     tics = 0.05;        /* eg 0, .05, .10, ... */
  1189.     else if (posns > 20)
  1190.     tics = 0.1;        /* eg 0, .1, .2, ... */
  1191.     else if (posns > 10)
  1192.     tics = 0.2;        /* eg 0,0.2,0.4,... */
  1193.     else if (posns > 4)
  1194.     tics = 0.5;        /* 0,0.5,1, */
  1195.     else if (posns > 1)
  1196.     tics = 1;        /* 0,1,2,.... */
  1197.     else if (posns > 0.5)
  1198.     tics = 2;        /* 0, 2, 4, 6 */
  1199.     else
  1200.     /* getting desperate... the ceil is to make sure we
  1201.      * go over rather than under - eg plot [-10:10] x*x
  1202.      * gives a range of about 99.999 - tics=xnorm gives
  1203.      * tics at 0, 99.99 and 109.98  - BAD !
  1204.      * This way, inaccuracy the other way will round
  1205.      * up (eg 0->100.0001 => tics at 0 and 101
  1206.      * I think latter is better than former
  1207.      */
  1208.     tics = ceil(xnorm);
  1209.  
  1210.     return (tics * dbl_raise(10.0, fl));
  1211. }
  1212. /*}}} */
  1213.  
  1214. /*{{{  make_tics() */
  1215. static double make_tics(axis, guide)
  1216. int axis, guide;
  1217. {
  1218.     register double xr, tic, l10;
  1219.  
  1220.     xr = fabs(min_array[axis] - max_array[axis]);
  1221.  
  1222.     l10 = log10(xr);
  1223.     tic = set_tic(l10, guide);
  1224.     if (log_array[axis] && tic < 1.0)
  1225.     tic = 1.0;
  1226.     if (datatype[axis] == TIME) {
  1227.     struct tm ftm, etm;
  1228.     /* this is not fun */
  1229.     ggmtime(&ftm, (double) min_array[axis]);
  1230.     ggmtime(&etm, (double) max_array[axis]);
  1231.  
  1232.     timelevel[axis] = 0;    /* seconds */
  1233.     if (tic > 20) {
  1234.         /* turn tic into units of minutes */
  1235.         tic = set_tic(log10(xr / 60.0), guide) * 60;
  1236.         timelevel[axis] = 1;    /* minutes */
  1237.     }
  1238.     if (tic > 20 * 60) {
  1239.         /* turn tic into units of hours */
  1240.         tic = set_tic(log10(xr / 3600.0), guide) * 3600;
  1241.         timelevel[axis] = 2;    /* hours */
  1242.     }
  1243.     if (tic > 2 * 3600) {
  1244.         /* need some tickling */
  1245.         tic = set_tic(log10(xr / (3 * 3600.0)), guide) * 3 * 3600;
  1246.     }
  1247.     if (tic > 6 * 3600) {
  1248.         /* turn tic into units of days */
  1249.         tic = set_tic(log10(xr / DAY_SEC), guide) * DAY_SEC;
  1250.         timelevel[axis] = 3;    /* days */
  1251.     }
  1252.     if (tic > 3 * DAY_SEC) {
  1253.         /* turn tic into units of weeks */
  1254.         tic = set_tic(log10(xr / WEEK_SEC), guide) * WEEK_SEC;
  1255.         if (tic < WEEK_SEC) {    /* force */
  1256.         tic = WEEK_SEC;
  1257.         }
  1258.         timelevel[axis] = 4;    /* weeks */
  1259.     }
  1260.     if (tic > 3 * WEEK_SEC) {
  1261.         /* turn tic into units of month */
  1262.         tic = set_tic(log10(xr / MON_SEC), guide) * MON_SEC;
  1263.         if (tic < MON_SEC) {    /* force */
  1264.         tic = MON_SEC;
  1265.         }
  1266.         timelevel[axis] = 5;    /* month */
  1267.     }
  1268.     if (tic > 2 * MON_SEC) {
  1269.         /* turn tic into units of month */
  1270.         tic = set_tic(log10(xr / (3 * MON_SEC)), guide) * 3 * MON_SEC;
  1271.     }
  1272.     if (tic > 6 * MON_SEC) {
  1273.         /* turn tic into units of years */
  1274.         tic = set_tic(log10(xr / YEAR_SEC), guide) * YEAR_SEC;
  1275.         if (tic < (YEAR_SEC / 2)) {
  1276.         tic = YEAR_SEC / 2;
  1277.         }
  1278.         timelevel[axis] = 6;    /* year */
  1279.     }
  1280.     }
  1281.     return (tic);
  1282. }
  1283. /*}}} */
  1284.  
  1285.  
  1286. void do_plot(plots, pcount)
  1287. struct curve_points *plots;
  1288. int pcount;            /* count of plots in linked list */
  1289. {
  1290.  
  1291. /* BODGES BEFORE I FIX IT UP */
  1292. #define ytic ticstep[y_axis]
  1293. #define xtic ticstep[x_axis]
  1294.  
  1295.     register struct termentry *t = term;
  1296.     register int curve;
  1297.     int axis_zero[AXIS_ARRAY_SIZE];    /* axes in terminal coords for FIRST_X_AXIS, etc */
  1298.     register struct curve_points *this_plot = NULL;
  1299.     register int xl = 0, yl = 0;    /* avoid gcc -Wall warning */
  1300.     register int key_count = 0;
  1301.     /* only a Pyramid would have this many registers! */
  1302.     struct text_label *this_label;
  1303.     struct arrow_def *this_arrow;
  1304.     TBOOLEAN scaling;
  1305.     char *s, *e;
  1306.  
  1307.     /* so that macros for x_min etc pick up correct values
  1308.      * until this is done properly
  1309.      */
  1310.  
  1311.     x_axis = FIRST_X_AXIS;
  1312.     y_axis = FIRST_Y_AXIS;
  1313.  
  1314. /*      Apply the desired viewport offsets. */
  1315.     if (y_min < y_max) {
  1316.     y_min -= boff;
  1317.     y_max += toff;
  1318.     } else {
  1319.     y_max -= boff;
  1320.     y_min += toff;
  1321.     }
  1322.     if (x_min < x_max) {
  1323.     x_min -= loff;
  1324.     x_max += roff;
  1325.     } else {
  1326.     x_max -= loff;
  1327.     x_min += roff;
  1328.     }
  1329.  
  1330.     /*
  1331.      * In the beginning, this "empty range" test was for exact
  1332.      * equality, eg  y_min == y_max , but that caused an infinite
  1333.      * loop once.  Then the test was changed to check for being
  1334.      * within the 'zero' threshold,  fabs(y_max - y_min) < zero) ,
  1335.      * but that prevented plotting data with ranges below 'zero'.
  1336.      * Now it's an absolute equality test again, since  fixup_range()
  1337.      * should have widened empty ranges before we get here.
  1338.      */
  1339.     if (x_min == x_max)
  1340.     int_error("x_min should not equal x_max!", NO_CARET);
  1341.     if (y_min == y_max)
  1342.     int_error("y_min should not equal y_max!", NO_CARET);
  1343.  
  1344.     term_init();        /* may set xmax/ymax */
  1345.  
  1346.     /* compute boundary for plot (xleft, xright, ytop, ybot)
  1347.      * also calculates tics, since xtics depend on xleft
  1348.      * but xleft depends on ytics. Boundary calculations
  1349.      * depend on term->v_char etc, so terminal must be
  1350.      * initialised.
  1351.      */
  1352.  
  1353.     scaling = (*t->scale) (xsize, ysize);
  1354.  
  1355.     boundary(scaling, plots, pcount);
  1356.  
  1357.     screen_ok = FALSE;
  1358.  
  1359.     term_start_plot();
  1360.  
  1361. /* DRAW TICS AND GRID */
  1362.     term_apply_lp_properties(&border_lp);    /* border linetype */
  1363.     largest_polar_circle = 0;
  1364.  
  1365.     /* select first mapping */
  1366.     x_axis = FIRST_X_AXIS;
  1367.     y_axis = FIRST_Y_AXIS;
  1368.  
  1369.     /* label first y axis tics */
  1370.     if (ytics) {
  1371.     int axis = map_x(ZERO);
  1372.     /* set the globals ytick2d_callback() needs */
  1373.  
  1374.     if (rotate_ytics && (*t->text_angle) (1)) {
  1375.         tic_hjust = CENTRE;
  1376.         tic_vjust = JUST_BOT;
  1377.         rotate_tics = 1;    /* HBB 980629 */
  1378.         ytic_x += t->v_char / 2;
  1379.     } else {
  1380.         tic_hjust = RIGHT;
  1381.         tic_vjust = JUST_CENTRE;
  1382.         rotate_tics = 0;    /* HBB 980629 */
  1383.     }
  1384.  
  1385.     if (ytics & TICS_MIRROR)
  1386.         tic_mirror = xright;
  1387.     else
  1388.         tic_mirror = -1;    /* no thank you */
  1389.  
  1390.     if ((ytics & TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
  1391.         tic_start = axis;
  1392.         tic_direction = -1;
  1393.         if (ytics & TICS_MIRROR)
  1394.         tic_mirror = tic_start;
  1395.         /* put text at boundary if axis is close to boundary */
  1396.         tic_text = (((tic_start - xleft) > (3 * t->h_char)) ? tic_start : xleft) - t->h_char;
  1397.     } else {
  1398.         tic_start = xleft;
  1399.         tic_direction = tic_in ? 1 : -1;
  1400.         tic_text = ytic_x;
  1401.     }
  1402.     /* go for it */
  1403.     gen_tics(FIRST_Y_AXIS, &yticdef,
  1404.          work_grid.l_type & (GRID_Y | GRID_MY),
  1405.          mytics, mytfreq, ytick2d_callback);
  1406.     (*t->text_angle) (0);    /* reset rotation angle */
  1407.  
  1408.     }
  1409.     /* label first x axis tics */
  1410.     if (xtics) {
  1411.     int axis = map_y(ZERO);
  1412.     /* set the globals xtick2d_callback() needs */
  1413.  
  1414.     if (rotate_xtics && (*t->text_angle) (1)) {
  1415.         tic_hjust = RIGHT;
  1416.         tic_vjust = JUST_CENTRE;
  1417.         rotate_tics = 1;    /* HBB 980629 */
  1418.     } else {
  1419.         tic_hjust = CENTRE;
  1420.         tic_vjust = JUST_TOP;
  1421.         rotate_tics = 0;    /* HBB 980629 */
  1422.     }
  1423.  
  1424.     if (xtics & TICS_MIRROR)
  1425.         tic_mirror = ytop;
  1426.     else
  1427.         tic_mirror = -1;    /* no thank you */
  1428.     if ((xtics & TICS_ON_AXIS) && !log_array[FIRST_Y_AXIS] && inrange(axis, ybot, ytop)) {
  1429.         tic_start = axis;
  1430.         tic_direction = -1;
  1431.         if (xtics & TICS_MIRROR)
  1432.         tic_mirror = tic_start;
  1433.         /* put text at boundary if axis is close to boundary */
  1434.         if (tic_start - ybot > 2 * t->v_char)
  1435.         tic_text = tic_start - ticscale * t->v_tic - t->v_char;
  1436.         else
  1437.         tic_text = ybot - t->v_char;
  1438.     } else {
  1439.         tic_start = ybot;
  1440.         tic_direction = tic_in ? 1 : -1;
  1441.         tic_text = xtic_y;
  1442.     }
  1443.     /* go for it */
  1444.     gen_tics(FIRST_X_AXIS, &xticdef,
  1445.          work_grid.l_type & (GRID_X | GRID_MX),
  1446.          mxtics, mxtfreq, xtick2d_callback);
  1447.     (*t->text_angle) (0);    /* reset rotation angle */
  1448.     }
  1449.     /* select second mapping */
  1450.     x_axis = SECOND_X_AXIS;
  1451.     y_axis = SECOND_Y_AXIS;
  1452.  
  1453.     /* label second y axis tics */
  1454.     if (y2tics) {
  1455.     /* set the globalss ytick2d_callback() needs */
  1456.     int axis = map_x(ZERO);
  1457.  
  1458.     if (rotate_y2tics && (*t->text_angle) (1)) {
  1459.         tic_hjust = CENTRE;
  1460.         tic_vjust = JUST_TOP;
  1461.         rotate_tics = 1;    /* HBB 980629 */
  1462.     } else {
  1463.         tic_hjust = LEFT;
  1464.         tic_vjust = JUST_CENTRE;
  1465.         rotate_tics = 0;    /* HBB 980629 */
  1466.     }
  1467.  
  1468.     if (y2tics & TICS_MIRROR)
  1469.         tic_mirror = xleft;
  1470.     else
  1471.         tic_mirror = -1;    /* no thank you */
  1472.     if ((y2tics & TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
  1473.         tic_start = axis;
  1474.         tic_direction = 1;
  1475.         if (y2tics & TICS_MIRROR)
  1476.         tic_mirror = tic_start;
  1477.         /* put text at boundary if axis is close to boundary */
  1478.         tic_text = (((xright - tic_start) > (3 * t->h_char)) ? tic_start : xright) + t->h_char;
  1479.     } else {
  1480.         tic_start = xright;
  1481.         tic_direction = tic_in ? -1 : 1;
  1482.         tic_text = y2tic_x;
  1483.     }
  1484.     /* go for it */
  1485.     gen_tics(SECOND_Y_AXIS, &y2ticdef,
  1486.          work_grid.l_type & (GRID_Y2 | GRID_MY2),
  1487.          my2tics, my2tfreq, ytick2d_callback);
  1488.     (*t->text_angle) (0);    /* reset rotation angle */
  1489.     }
  1490.     /* label second x axis tics */
  1491.     if (x2tics) {
  1492.     int axis = map_y(ZERO);
  1493.     /* set the globals xtick2d_callback() needs */
  1494.  
  1495.     if (rotate_x2tics && (*t->text_angle) (1)) {
  1496.         tic_hjust = LEFT;
  1497.         tic_vjust = JUST_CENTRE;
  1498.         rotate_tics = 1;    /* HBB 980629 */
  1499.     } else {
  1500.         tic_hjust = CENTRE;
  1501.         tic_vjust = JUST_BOT;
  1502.         rotate_tics = 0;    /* HBB 980629 */
  1503.     }
  1504.  
  1505.     if (x2tics & TICS_MIRROR)
  1506.         tic_mirror = ybot;
  1507.     else
  1508.         tic_mirror = -1;    /* no thank you */
  1509.     if ((x2tics & TICS_ON_AXIS) && !log_array[SECOND_Y_AXIS] && inrange(axis, ybot, ytop)) {
  1510.         tic_start = axis;
  1511.         tic_direction = 1;
  1512.         if (x2tics & TICS_MIRROR)
  1513.         tic_mirror = tic_start;
  1514.         /* put text at boundary if axis is close to boundary */
  1515.         tic_text = (((ytop - tic_start) > (2 * t->v_char)) ? tic_start : ytop) + t->v_char;
  1516.     } else {
  1517.         tic_start = ytop;
  1518.         tic_direction = tic_in ? -1 : 1;
  1519.         tic_text = x2tic_y;
  1520.     }
  1521.     /* go for it */
  1522.     gen_tics(SECOND_X_AXIS, &x2ticdef,
  1523.          work_grid.l_type & (GRID_X2 | GRID_MX2),
  1524.          mx2tics, mx2tfreq, xtick2d_callback);
  1525.     (*t->text_angle) (0);    /* reset rotation angle */
  1526.     }
  1527.     /* select first mapping */
  1528.     x_axis = FIRST_X_AXIS;
  1529.     y_axis = FIRST_Y_AXIS;
  1530.  
  1531. /* RADIAL LINES FOR POLAR GRID */
  1532.  
  1533.     /* note that draw_clip_line takes unsigneds, but (fortunately)
  1534.      * clip_line takes signeds
  1535.      */
  1536.     if (polar_grid_angle) {
  1537.     double theta = 0;
  1538.     int ox = map_x(0);
  1539.     int oy = map_y(0);
  1540.     term_apply_lp_properties(&grid_lp);
  1541.     for (theta = 0; theta < 6.29; theta += polar_grid_angle) {
  1542.         /* copy ox in case it gets moved (but it shouldn't) */
  1543.         int oox = ox;
  1544.         int ooy = oy;
  1545.         int x = map_x(largest_polar_circle * cos(theta));
  1546.         int y = map_y(largest_polar_circle * sin(theta));
  1547.         if (clip_line(&oox, &ooy, &x, &y)) {
  1548.         (*t->move) ((unsigned int) oox, (unsigned int) ooy);
  1549.         (*t->vector) ((unsigned int) x, (unsigned int) y);
  1550.         }
  1551.     }
  1552.     draw_clip_line(ox, oy,
  1553.                map_x(largest_polar_circle * cos(theta)),
  1554.                map_y(largest_polar_circle * sin(theta)));
  1555.     }
  1556. /* DRAW AXES */
  1557.  
  1558.     /* after grid so that axes linetypes are on top */
  1559.  
  1560.     x_axis = FIRST_X_AXIS;
  1561.     y_axis = FIRST_Y_AXIS;    /* chose scaling */
  1562.     axis_zero[FIRST_Y_AXIS] = map_y(0.0);
  1563.     axis_zero[FIRST_X_AXIS] = map_x(0.0);
  1564.  
  1565.     if (axis_zero[FIRST_Y_AXIS] < ybot || is_log_y)
  1566.     axis_zero[FIRST_Y_AXIS] = ybot;        /* save for impulse plotting */
  1567.     else if (axis_zero[FIRST_Y_AXIS] >= ytop)
  1568.     axis_zero[FIRST_Y_AXIS] = ytop;
  1569.     else if (xzeroaxis.l_type > -3) {
  1570.     term_apply_lp_properties(&xzeroaxis);
  1571.     (*t->move) (xleft, axis_zero[FIRST_Y_AXIS]);
  1572.     (*t->vector) (xright, axis_zero[FIRST_Y_AXIS]);
  1573.     }
  1574.     if ((yzeroaxis.l_type > -3) && !is_log_x
  1575.     && axis_zero[FIRST_X_AXIS] >= xleft
  1576.     && axis_zero[FIRST_X_AXIS] < xright) {
  1577.     term_apply_lp_properties(&yzeroaxis);
  1578.     (*t->move) (axis_zero[FIRST_X_AXIS], ybot);
  1579.     (*t->vector) (axis_zero[FIRST_X_AXIS], ytop);
  1580.     }
  1581.     x_axis = SECOND_X_AXIS;
  1582.     y_axis = SECOND_Y_AXIS;    /* chose scaling */
  1583.     axis_zero[SECOND_Y_AXIS] = map_y(0.0);
  1584.     axis_zero[SECOND_X_AXIS] = map_x(0.0);
  1585.  
  1586.     if (axis_zero[SECOND_Y_AXIS] < ybot || is_log_y2)
  1587.     axis_zero[SECOND_Y_AXIS] = ybot;  /* save for impulse plotting */
  1588.     else if (axis_zero[SECOND_Y_AXIS] >= ytop)
  1589.     axis_zero[SECOND_Y_AXIS] = ytop;
  1590.     else if (x2zeroaxis.l_type > -3) {
  1591.     term_apply_lp_properties(&x2zeroaxis);
  1592.     (*t->move) (xleft, axis_zero[SECOND_Y_AXIS]);
  1593.     (*t->vector) (xright, axis_zero[SECOND_Y_AXIS]);
  1594.     }
  1595.     if ((y2zeroaxis.l_type > -3) && !is_log_x2 &&
  1596.     axis_zero[SECOND_X_AXIS] >= xleft &&
  1597.     axis_zero[SECOND_X_AXIS] < xright) {
  1598.     term_apply_lp_properties(&y2zeroaxis);
  1599.     (*t->move) (axis_zero[SECOND_X_AXIS], ybot);
  1600.     (*t->vector) (axis_zero[SECOND_X_AXIS], ytop);
  1601.     }
  1602.     /* DRAW PLOT BORDER */
  1603.     if (draw_border) {
  1604.     /* HBB 980609: just in case: move over to border linestyle only
  1605.      * if border is to be drawn */
  1606.     term_apply_lp_properties(&border_lp);    /* border linetype */
  1607.     (*t->move) (xleft, ybot);
  1608.     if (border_south) {
  1609.         (*t->vector) (xright, ybot);
  1610.     } else {
  1611.         (*t->move) (xright, ybot);
  1612.     }
  1613.     if (border_east) {
  1614.         (*t->vector) (xright, ytop);
  1615.     } else {
  1616.         (*t->move) (xright, ytop);
  1617.     }
  1618.     if (border_north) {
  1619.         (*t->vector) (xleft, ytop);
  1620.     } else {
  1621.         (*t->move) (xleft, ytop);
  1622.     }
  1623.     if (border_west) {
  1624.         (*t->vector) (xleft, ybot);
  1625.     } else {
  1626.         (*t->move) (xleft, ybot);
  1627.     }
  1628.     }
  1629. /* YLABEL */
  1630.     if (*ylabel.text) {
  1631.     /* we worked out x-posn in boundary() */
  1632.     if ((*t->text_angle) (1)) {
  1633.         unsigned int x = ylabel_x + (t->v_char / 2);
  1634.         unsigned int y = (ytop + ybot) / 2 + ylabel.yoffset * (t->h_char);
  1635.         write_multiline(x, y, ylabel.text, CENTRE, JUST_TOP, 1, ylabel.font);
  1636.         (*t->text_angle) (0);
  1637.     } else {
  1638.         /* really bottom just, but we know number of lines 
  1639.            so we need to adjust x-posn by one line */
  1640.         unsigned int x = ylabel_x;
  1641.         unsigned int y = ylabel_y;
  1642.         write_multiline(x, y, ylabel.text, LEFT, JUST_TOP, 0, ylabel.font);
  1643.     }
  1644.     }
  1645. /* Y2LABEL */
  1646.     if (*y2label.text) {
  1647.     /* we worked out coordinates in boundary() */
  1648.     if ((*t->text_angle) (1)) {
  1649.         unsigned int x = y2label_x + (t->v_char / 2) - 1;
  1650.         unsigned int y = (ytop + ybot) / 2 + y2label.yoffset * (t->h_char);
  1651.         write_multiline(x, y, y2label.text, CENTRE, JUST_TOP, 1, y2label.font);
  1652.         (*t->text_angle) (0);
  1653.     } else {
  1654.         /* really bottom just, but we know number of lines */
  1655.         unsigned int x = y2label_x;
  1656.         unsigned int y = y2label_y;
  1657.         write_multiline(x, y, y2label.text, RIGHT, JUST_TOP, 0, y2label.font);
  1658.     }
  1659.     }
  1660. /* XLABEL */
  1661.     if (*xlabel.text) {
  1662.     unsigned int x = (xright + xleft) / 2 + xlabel.xoffset * (t->h_char);
  1663.     unsigned int y = xlabel_y - t->v_char / 2;    /* HBB */
  1664.     write_multiline(x, y, xlabel.text, CENTRE, JUST_TOP, 0, xlabel.font);
  1665.     }
  1666. /* PLACE TITLE */
  1667.     if (*title.text) {
  1668.     /* we worked out y-coordinate in boundary() */
  1669.     unsigned int x = (xleft + xright) / 2 + title.xoffset * t->h_char;
  1670.     unsigned int y = title_y - t->v_char / 2;
  1671.     write_multiline(x, y, title.text, CENTRE, JUST_TOP, 0, title.font);
  1672.     }
  1673. /* X2LABEL */
  1674.     if (*x2label.text) {
  1675.     /* we worked out y-coordinate in boundary() */
  1676.     unsigned int x = (xright + xleft) / 2 + x2label.xoffset * (t->h_char);
  1677.     unsigned int y = x2label_y - t->v_char / 2 - 1;
  1678.     write_multiline(x, y, x2label.text, CENTRE, JUST_TOP, 0, x2label.font);
  1679.     }
  1680. /* PLACE TIMEDATE */
  1681.     if (*timelabel.text) {
  1682.     /* we worked out coordinates in boundary() */
  1683.     char *str;
  1684.     time_t now;
  1685.     unsigned int x = time_x;
  1686.     unsigned int y = time_y;
  1687.     time(&now);
  1688.     /* there is probably now way to find out in advance how many
  1689.      * chars strftime() writes */
  1690.     str = gp_alloc(MAX_LINE_LEN + 1, "timelabel.text");
  1691.     strftime(str, MAX_LINE_LEN, timelabel.text, localtime(&now));
  1692.  
  1693.     if (timelabel_rotate && (*t->text_angle) (1)) {
  1694.         x += t->v_char / 2;    /* HBB */
  1695.         if (timelabel_bottom)
  1696.         write_multiline(x, y, str, LEFT, JUST_TOP, 1, timelabel.font);
  1697.         else
  1698.         write_multiline(x, y, str, RIGHT, JUST_TOP, 1, timelabel.font);
  1699.         (*t->text_angle) (0);
  1700.     } else {
  1701.         y -= t->v_char / 2;    /* HBB */
  1702.         if (timelabel_bottom)
  1703.         write_multiline(x, y, str, LEFT, JUST_BOT, 0, timelabel.font);
  1704.         else
  1705.         write_multiline(x, y, str, LEFT, JUST_TOP, 0, timelabel.font);
  1706.     }
  1707.     }
  1708. /* PLACE LABELS */
  1709.     for (this_label = first_label; this_label != NULL;
  1710.      this_label = this_label->next) {
  1711.     unsigned int x, y;
  1712.     map_position(&this_label->place, &x, &y, "label");
  1713.     if (this_label->rotate && (*t->text_angle) (1)) {
  1714.         write_multiline(x, y, this_label->text, this_label->pos, JUST_TOP, 1, this_label->font);
  1715.         (*t->text_angle) (0);
  1716.     } else {
  1717.         write_multiline(x, y, this_label->text, this_label->pos, JUST_TOP, 0, this_label->font);
  1718.     }
  1719.     }
  1720.  
  1721. /* PLACE ARROWS */
  1722.     for (this_arrow = first_arrow; this_arrow != NULL;
  1723.      this_arrow = this_arrow->next) {
  1724.     unsigned int sx, sy, ex, ey;
  1725.     map_position(&this_arrow->start, &sx, &sy, "arrow");
  1726.     map_position(&this_arrow->end, &ex, &ey, "arrow");
  1727.  
  1728.     term_apply_lp_properties(&(this_arrow->lp_properties));
  1729.     (*t->arrow) (sx, sy, ex, ey, this_arrow->head);
  1730.     }
  1731.  
  1732. /* WORK OUT KEY SETTINGS AND DO KEY TITLE / BOX */
  1733.  
  1734.  
  1735.     if (lkey) {            /* may have been cancelled if something went wrong */
  1736.     /* just use key_xl etc worked out in boundary() */
  1737.     xl = key_xl + key_size_left;
  1738.     yl = key_yt;
  1739.  
  1740.     if (*key_title) {
  1741.         char *ss = gp_alloc(strlen(key_title) + 2, "tmp string ss");
  1742.         strcpy(ss, key_title);
  1743.         strcat(ss, "\n");
  1744.  
  1745.         s = ss;
  1746.         yl -= t->v_char / 2;
  1747.         while ((e = (char *) strchr(s, '\n')) != NULL) {
  1748.         *e = '\0';
  1749.         if (key_just == JLEFT) {
  1750.             (*t->justify_text) (LEFT);
  1751.             (*t->put_text) (xl + key_text_left, yl, s);
  1752.         } else {
  1753.             if ((*t->justify_text) (RIGHT)) {
  1754.             (*t->put_text) (xl + key_text_right, yl, s);
  1755.             } else {
  1756.             int x = xl + key_text_right - (t->h_char) * strlen(s);
  1757.             if (key_hpos == TOUT || inrange(x, xleft, xright))
  1758.                 (*t->put_text) (x, yl, s);
  1759.             }
  1760.         }
  1761.         s = ++e;
  1762.         yl -= t->v_char;
  1763.         }
  1764.         yl += t->v_char / 2;
  1765.         free(ss);
  1766.     }
  1767.     yl_ref = yl -= key_entry_height / 2;    /* centralise the keys */
  1768.     key_count = 0;
  1769.  
  1770.     if (key_box.l_type > -3) {
  1771.         term_apply_lp_properties(&key_box);
  1772.         (*t->move) (key_xl, key_yb);
  1773.         (*t->vector) (key_xl, key_yt);
  1774.         (*t->vector) (key_xr, key_yt);
  1775.         (*t->vector) (key_xr, key_yb);
  1776.         (*t->vector) (key_xl, key_yb);
  1777.         /* draw a horizontal line between key title
  1778.    and first entry                          *//* JFi */
  1779.         (*t->move) (key_xl, key_yt - (ktitl_lines) * t->v_char);    /* JFi */
  1780.         (*t->vector) (key_xr, key_yt - (ktitl_lines) * t->v_char);    /* JFi */
  1781.     }
  1782.     }                /* lkey */
  1783.     /* DRAW CURVES */
  1784.     this_plot = plots;
  1785.     for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) {
  1786.     int localkey = lkey;    /* a local copy */
  1787.  
  1788.     /* set scaling for this plot's axes */
  1789.     x_axis = this_plot->x_axis;
  1790.     y_axis = this_plot->y_axis;
  1791.  
  1792.     term_apply_lp_properties(&(this_plot->lp_properties));
  1793.  
  1794.     if (this_plot->title && !*this_plot->title) {
  1795.         localkey = 0;
  1796.     } else {
  1797.         if (localkey != 0 && this_plot->title) {
  1798.         key_count++;
  1799.         if (key_just == JLEFT) {
  1800.             (*t->justify_text) (LEFT);
  1801.             (*t->put_text) (xl + key_text_left,
  1802.                     yl, this_plot->title);
  1803.         } else {
  1804.             if ((*t->justify_text) (RIGHT)) {
  1805.             (*t->put_text) (xl + key_text_right,
  1806.                     yl, this_plot->title);
  1807.             } else {
  1808.             int x = xl + key_text_right - (t->h_char) * strlen(this_plot->title);
  1809.             if (key_hpos == TOUT ||
  1810.                 i_inrange(x, xleft, xright))
  1811.                 (*t->put_text) (x, yl, this_plot->title);
  1812.             }
  1813.         }
  1814.  
  1815.         /* draw sample depending on bits set in plot_style */
  1816.         if ((this_plot->plot_style & 1) ||
  1817.             ((this_plot->plot_style & 4) && this_plot->plot_type == DATA)) {    /* errors for data plots only */
  1818.             (*t->move) (xl + key_sample_left, yl);
  1819.             (*t->vector) (xl + key_sample_right, yl);
  1820.         }
  1821.         /* oops - doing the point sample now breaks postscript
  1822.          * terminal for example, which changes current line style
  1823.          * when drawing a point, but does not restore it.
  1824.          * We simply draw the point sample after plotting
  1825.          */
  1826.  
  1827.         if (this_plot->plot_type == DATA &&
  1828.             (this_plot->plot_style & 4) &&
  1829.             bar_size > 0.0) {
  1830.             (*t->move) (xl + key_sample_left, yl + ERRORBARTIC);
  1831.             (*t->vector) (xl + key_sample_left, yl - ERRORBARTIC);
  1832.             (*t->move) (xl + key_sample_right, yl + ERRORBARTIC);
  1833.             (*t->vector) (xl + key_sample_right, yl - ERRORBARTIC);
  1834.         }
  1835.         }
  1836.     }
  1837.  
  1838.     /* and now the curves, plus any special key requirements */
  1839.     /* be sure to draw all lines before drawing any points */
  1840.  
  1841.     switch (this_plot->plot_style) {
  1842.         /*{{{  IMPULSE */
  1843.     case IMPULSES:
  1844.         plot_impulses(this_plot, axis_zero[x_axis], axis_zero[y_axis]);
  1845.         break;
  1846.         /*}}} */
  1847.         /*{{{  LINES */
  1848.     case LINES:
  1849.         plot_lines(this_plot);
  1850.         break;
  1851.         /*}}} */
  1852.         /*{{{  STEPS */
  1853.     case STEPS:
  1854.         plot_steps(this_plot);
  1855.         break;
  1856.         /*}}} */
  1857.         /*{{{  FSTEPS */
  1858.     case FSTEPS:
  1859.         plot_fsteps(this_plot);
  1860.         break;
  1861.         /*}}} */
  1862.         /*{{{  HISTEPS */
  1863.     case HISTEPS:
  1864.         plot_histeps(this_plot);
  1865.         break;
  1866.         /*}}} */
  1867.         /*{{{  POINTSTYLE */
  1868.     case POINTSTYLE:
  1869.         plot_points(this_plot);
  1870.         break;
  1871.         /*}}} */
  1872.         /*{{{  LINESPOINTS */
  1873.     case LINESPOINTS:
  1874.         plot_lines(this_plot);
  1875.         plot_points(this_plot);
  1876.         break;
  1877.         /*}}} */
  1878.         /*{{{  DOTS */
  1879.     case DOTS:
  1880.         if (localkey != 0 && this_plot->title) {
  1881.         (*t->point) (xl + key_point_offset, yl, -1);
  1882.         }
  1883.         plot_dots(this_plot);
  1884.         break;
  1885.         /*}}} */
  1886.         /*{{{  YERRORBARS */
  1887.     case YERRORBARS:{
  1888.         plot_bars(this_plot);
  1889.         plot_points(this_plot);
  1890.         break;
  1891.         }
  1892.         /*}}} */
  1893.         /*{{{  XERRORBARS */
  1894.     case XERRORBARS:{
  1895.         plot_bars(this_plot);
  1896.         plot_points(this_plot);
  1897.         }
  1898.         /*}}} */
  1899.         /*{{{  XYERRORBARS */
  1900.     case XYERRORBARS:
  1901.         plot_bars(this_plot);
  1902.         plot_points(this_plot);
  1903.         break;
  1904.  
  1905.         /*}}} */
  1906.         /*{{{  BOXXYERROR */
  1907.     case BOXXYERROR:
  1908.         plot_boxes(this_plot, axis_zero[y_axis]);
  1909.         break;
  1910.         /*}}} */
  1911.         /*{{{  BOXERROR (falls through to) */
  1912.     case BOXERROR:
  1913.         plot_bars(this_plot);
  1914.         /* no break */
  1915.  
  1916.         /*}}} */
  1917.         /*{{{  BOXES */
  1918.     case BOXES:
  1919.         plot_boxes(this_plot, axis_zero[y_axis]);
  1920.         break;
  1921.         /*}}} */
  1922.         /*{{{  VECTOR */
  1923.     case VECTOR:
  1924.         plot_vectors(this_plot);
  1925.         break;
  1926.  
  1927.         /*}}} */
  1928.         /*{{{  FINANCEBARS */
  1929.     case FINANCEBARS:
  1930.         plot_f_bars(this_plot);
  1931.         break;
  1932.         /*}}} */
  1933.         /*{{{  CANDLESTICKS */
  1934.     case CANDLESTICKS:
  1935.         plot_c_bars(this_plot);
  1936.         break;
  1937.         /*}}} */
  1938.     }
  1939.  
  1940.  
  1941.     if (localkey && this_plot->title) {
  1942.         /* we deferred point sample until now */
  1943.         if (this_plot->plot_style & 2)
  1944.         (*t->point) (xl + key_point_offset, yl,
  1945.                  this_plot->lp_properties.p_type);
  1946.  
  1947.         if (key_count >= key_rows) {
  1948.         yl = yl_ref;
  1949.         xl += key_col_wth;
  1950.         key_count = 0;
  1951.         } else
  1952.         yl = yl - key_entry_height;
  1953.     }
  1954.     }
  1955.  
  1956.     term_end_plot();
  1957. }
  1958.  
  1959.  
  1960. /* BODGES */
  1961. #undef ytic
  1962. #undef xtic
  1963.  
  1964. /* plot_impulses:
  1965.  * Plot the curves in IMPULSES style
  1966.  */
  1967.  
  1968. static void plot_impulses(plot, yaxis_x, xaxis_y)
  1969. struct curve_points *plot;
  1970. int yaxis_x, xaxis_y;
  1971. {
  1972.     int i;
  1973.     int x, y;
  1974.     struct termentry *t = term;
  1975.  
  1976.     for (i = 0; i < plot->p_count; i++) {
  1977.     switch (plot->points[i].type) {
  1978.     case INRANGE:{
  1979.         x = map_x(plot->points[i].x);
  1980.         y = map_y(plot->points[i].y);
  1981.         break;
  1982.         }
  1983.     case OUTRANGE:{
  1984.         if (!inrange(plot->points[i].x, x_min, x_max))
  1985.             continue;
  1986.         x = map_x(plot->points[i].x);
  1987.         if ((y_min < y_max
  1988.              && plot->points[i].y < y_min)
  1989.             || (y_max < y_min
  1990.             && plot->points[i].y > y_min))
  1991.             y = map_y(y_min);
  1992.         else
  1993.             y = map_y(y_max);
  1994.  
  1995.         break;
  1996.         }
  1997.     default:        /* just a safety */
  1998.     case UNDEFINED:{
  1999.         continue;
  2000.         }
  2001.     }
  2002.  
  2003.     if (polar)
  2004.         (*t->move) (yaxis_x, xaxis_y);
  2005.     else
  2006.         (*t->move) (x, xaxis_y);
  2007.     (*t->vector) (x, y);
  2008.     }
  2009.  
  2010. }
  2011.  
  2012. /* plot_lines:
  2013.  * Plot the curves in LINES style
  2014.  */
  2015. static void plot_lines(plot)
  2016. struct curve_points *plot;
  2017. {
  2018.     int i;            /* point index */
  2019.     int x, y;            /* point in terminal coordinates */
  2020.     struct termentry *t = term;
  2021.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  2022.     double ex, ey;        /* an edge point */
  2023.     double lx[2], ly[2];    /* two edge points */
  2024.  
  2025.     for (i = 0; i < plot->p_count; i++) {
  2026.     switch (plot->points[i].type) {
  2027.     case INRANGE:{
  2028.         x = map_x(plot->points[i].x);
  2029.         y = map_y(plot->points[i].y);
  2030.  
  2031.         if (prev == INRANGE) {
  2032.             (*t->vector) (x, y);
  2033.         } else if (prev == OUTRANGE) {
  2034.             /* from outrange to inrange */
  2035.             if (!clip_lines1) {
  2036.             (*t->move) (x, y);
  2037.      = time_tic_just    edge_intersect(plot->points, i, &ex, &ey);
  2038.             (*t->move) (map_x(ex), map_y(ey));
  2039.             (*t->vector) (x, y);
  2040.             }
  2041.         } else {    /* prev == UNDEFINED */
  2042.             (*t->move) (x, y);
  2043.             (*t->vector) (x, y);
  2044.         }
  2045.  
  2046.         break;
  2047.         }
  2048.     case OUTRANGE:{
  2049.         if (prev == INRANGE) {
  2050.             /* from inrange to outrange */
  2051.             if (clip_lines1) {
  2052.             edge_intersect(plot->points, i, &ex, &ey);
  2053.             (*t->vector) (map_x(ex), map_y(ey));
  2054.             }
  2055.         } else if (prev == OUTRANGE) {
  2056.             /* from outrange to outrange */
  2057.             if (clip_lines2) {
  2058.             if (two_edge_intersect(plot->points, i, lx, ly)) {
  2059.                 (*t->move) (map_x(lx[0]), map_y(ly[0]));
  2060.                 (*t->vector) (map_x(lx[1]), map_y(ly[1]));
  2061.             }
  2062.             }
  2063.         }
  2064.         break;
  2065.         }
  2066.     default:        /* just a safety */
  2067.     case UNDEFINED:{
  2068.         break;
  2069.         }
  2070.     }
  2071.     prev = plot->points[i].type;
  2072.     }
  2073. }
  2074.  
  2075. /* XXX - JG  */
  2076. /* plot_steps:                          
  2077.  * Plot the curves in STEPS style
  2078.  */
  2079. static void plot_steps(plot)
  2080. struct curve_points *plot;
  2081. {
  2082.     int i;            /* point index */
  2083.     int x, y;            /* point in terminal coordinates */
  2084.     struct termentry *t = term;
  2085.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  2086.     double ex, ey;        /* an edge point */
  2087.     double lx[2], ly[2];    /* two edge points */
  2088.     int yprev = 0;        /* previous point coordinates */
  2089.  
  2090.     for (i = 0; i < plot->p_count; i++) {
  2091.     switch (plot->points[i].type) {
  2092.     case INRANGE:{
  2093.         x = map_x(plot->points[i].x);
  2094.         y = map_y(plot->points[i].y);
  2095.  
  2096.         if (prev == INRANGE) {
  2097.             (*t->vector) (x, yprev);
  2098.             (*t->vector) (x, y);
  2099.         } else if (prev == OUTRANGE) {
  2100.             /* from outrange to inrange */
  2101.             if (!clip_lines1) {
  2102.             (*t->move) (x, y);
  2103.             } else {    /* find edge intersection */
  2104.             edge_intersect_steps(plot->points, i, &ex, &ey);
  2105.             (*t->move) (map_x(ex), map_y(ey));
  2106.             (*t->vector) (x, map_y(ey));
  2107.             (*t->vector) (x, y);
  2108.             }
  2109.         } else {    /* prev == UNDEFINED */
  2110.             (*t->move) (x, y);
  2111.             (*t->vector) (x, y);
  2112.         }
  2113.         yprev = y;
  2114.         break;
  2115.         }
  2116.     case OUTRANGE:{
  2117.         if (prev == INRANGE) {
  2118.             /* from inrange to outrange */
  2119.             if (clip_lines1) {
  2120.             edge_intersect_steps(plot->points, i, &ex, &ey);
  2121.             (*t->vector) (map_x(ex), yprev);
  2122.             (*t->vector) (map_x(ex), map_y(ey));
  2123.             }
  2124.         } else if (prev == OUTRANGE) {
  2125.             /* from outrange to outrange */
  2126.             if (clip_lines2) {
  2127.             if (two_edge_intersect_steps(plot->points, i, lx, ly)) {
  2128.                 (*t->move) (map_x(lx[0]), map_y(ly[0]));
  2129.                 (*t->vector) (map_x(lx[1]), map_y(ly[0]));
  2130.                 (*t->vector) (map_x(lx[1]), map_y(ly[1]));
  2131.             }
  2132.             }
  2133.         }
  2134.         break;
  2135.         }
  2136.     default:        /* just a safety */
  2137.     case UNDEFINED:{
  2138.         break;
  2139.         }
  2140.     }
  2141.     prev = plot->points[i].type;
  2142.     }
  2143. }
  2144. /* XXX - HOE  */
  2145. /* plot_fsteps:                         
  2146.  * Plot the curves in STEPS style by step on forward yvalue
  2147.  */
  2148. static void plot_fsteps(plot)
  2149. struct curve_points *plot;
  2150. {
  2151.     int i;            /* point index */
  2152.     int x, y;            /* point in terminal coordinates */
  2153.     struct termentry *t = term;
  2154.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  2155.     double ex, ey;        /* an edge point */
  2156.     double lx[2], ly[2];    /* two edge points */
  2157.     int xprev = 0;        /* previous point coordinates */
  2158.  
  2159.     for (i = 0; i < plot->p_count; i++) {
  2160.     switch (plot->points[i].type) {
  2161.     case INRANGE:{
  2162.         x = map_x(plot->points[i].x);
  2163.         y = map_y(plot->points[i].y);
  2164.  
  2165.         if (prev == INRANGE) {
  2166.             (*t->vector) (xprev, y);
  2167.             (*t->vector) (x, y);
  2168.         } else if (prev == OUTRANGE) {
  2169.             /* from outrange to inrange */
  2170.             if (!clip_lines1) {
  2171.             (*t->move) (x, y);
  2172.             } else {    /* find edge intersection */
  2173.             edge_intersect_fsteps(plot->points, i, &ex, &ey);
  2174.             (*t->move) (map_x(ex), map_y(ey));
  2175.             (*t->vector) (map_x(ex), y);
  2176.             (*t->vector) (x, y);
  2177.             }
  2178.         } else {    /* prev == UNDEFINED */
  2179.             (*t->move) (x, y);
  2180.             (*t->vector) (x, y);
  2181.         }
  2182.         xprev = x;
  2183.         break;
  2184.         }
  2185.     case OUTRANGE:{
  2186.         if (prev == INRANGE) {
  2187.             /* from inrange to outrange */
  2188.             if (clip_lines1) {
  2189.             edge_intersect_fsteps(plot->points, i, &ex, &ey);
  2190.             (*t->vector) (xprev, map_y(ey));
  2191.             (*t->vector) (map_x(ex), map_y(ey));
  2192.             }
  2193.         } else if (prev == OUTRANGE) {
  2194.             /* from outrange to outrange */
  2195.             if (clip_lines2) {
  2196.             if (two_edge_intersect_fsteps(plot->points, i, lx, ly)) {
  2197.                 (*t->move) (map_x(lx[0]), map_y(ly[0]));
  2198.                 (*t->vector) (map_x(lx[0]), map_y(ly[1]));
  2199.                 (*t->vector) (map_x(lx[1]), map_y(ly[1]));
  2200.             }
  2201.             }
  2202.         }
  2203.         break;
  2204.         }
  2205.     default:        /* just a safety */
  2206.     case UNDEFINED:{
  2207.         break;
  2208.         }
  2209.     }
  2210.     prev = plot->points[i].type;
  2211.     }
  2212. }
  2213.  
  2214. /* CAC  */
  2215. /* plot_histeps:                                
  2216.  * Plot the curves in HISTEPS style
  2217.  */
  2218. static void plot_histeps(plot)
  2219. struct curve_points *plot;
  2220. {
  2221.     int i;            /* point index */
  2222.     int hold, bigi;        /* indices for sorting */
  2223.     int xl, yl;            /* cursor position in terminal coordinates */
  2224.     struct termentry *t = term;
  2225.     double x, y, xn, yn;    /* point position */
  2226.     int *gl, goodcount;        /* array to hold list of valid points */
  2227.  
  2228.     /* preliminary count of points inside array */
  2229.     goodcount = 0;
  2230.     for (i = 0; i < plot->p_count; i++)
  2231.     if (plot->points[i].type == INRANGE ||
  2232.         plot->points[i].type == OUTRANGE)
  2233.         ++goodcount;
  2234.     if (goodcount < 2)
  2235.     return;            /* cannot plot less than 2 points */
  2236.  
  2237.     gl = (int *) gp_alloc(goodcount * sizeof(int), "histeps valid point mapping");
  2238.     if (gl == NULL)
  2239.     return;
  2240.  
  2241. /* fill gl array with indexes of valid (non-undefined) points.  */
  2242.     goodcount = 0;
  2243.     for (i = 0; i < plot->p_count; i++)
  2244.     if (plot->points[i].type == INRANGE ||
  2245.         plot->points[i].type == OUTRANGE) {
  2246.         gl[goodcount] = i;
  2247.         ++goodcount;
  2248.     }
  2249. /* sort the data */
  2250.     for (bigi = i = 1; i < goodcount;) {
  2251.     if (plot->points[gl[i]].x < plot->points[gl[i - 1]].x) {
  2252.         hold = gl[i];
  2253.         gl[i] = gl[i - 1];
  2254.         gl[i - 1] = hold;
  2255.         if (i > 1) {
  2256.         i--;
  2257.         continue;
  2258.         }
  2259.     }
  2260.     i = ++bigi;
  2261.     }
  2262.  
  2263.     x = (3.0 * plot->points[gl[0]].x - plot->points[gl[1]].x) / 2.0;
  2264.     y = 0.0;
  2265.  
  2266.     xl = map_x(x);
  2267.     yl = map_y(y);
  2268.     (*t->move) (xl, yl);
  2269.  
  2270.     for (i = 0; i < goodcount - 1; i++) {    /* loop over all points except last  */
  2271.  
  2272.     yn = plot->points[gl[i]].y;
  2273.     xn = (plot->points[gl[i]].x + plot->points[gl[i + 1]].x) / 2.0;
  2274.     histeps_vertical(&xl, &yl, x, y, yn);
  2275.     histeps_horizontal(&xl, &yl, x, xn, yn);
  2276.  
  2277.     x = xn;
  2278.     y = yn;
  2279.     }
  2280.  
  2281.     yn = plot->points[gl[i]].y;
  2282.     xn = (3.0 * plot->points[gl[i]].x - plot->points[gl[i - 1]].x) / 2.0;
  2283.     histeps_vertical(&xl, &yl, x, y, yn);
  2284.     histeps_horizontal(&xl, &yl, x, xn, yn);
  2285.     histeps_vertical(&xl, &yl, xn, yn, 0.0);
  2286.  
  2287.     free(gl);
  2288. }
  2289. /* CAC 
  2290.  * Draw vertical line for the histeps routine.
  2291.  * Performs clipping.
  2292.  */
  2293. static void histeps_vertical(xl, yl, x, y1, y2)
  2294. int *xl, *yl;            /* keeps track of "cursor" position */
  2295. double x, y1, y2;        /* coordinates of vertical line */
  2296. {
  2297.     struct termentry *t = term;
  2298.     /* global x_min, x_max, y_min, y_max */
  2299.     int xm, y1m, y2m;
  2300.  
  2301.     if ((y1 < y_min && y2 < y_min) ||
  2302.     (y1 > y_max && y2 > y_max) ||
  2303.     x < x_min ||
  2304.     x > x_max)
  2305.     return;
  2306.  
  2307.     if (y1 < y_min)
  2308.     y1 = y_min;
  2309.     if (y1 > y_max)
  2310.     y1 = y_max;
  2311.     if (y2 < y_min)
  2312.     y2 = y_min;
  2313.     if (y2 > y_max)
  2314.     y2 = y_max;
  2315.  
  2316.     xm = map_x(x);
  2317.     y1m = map_y(y1);
  2318.     y2m = map_y(y2);
  2319.  
  2320.     if (y1m != *yl || xm != *xl)
  2321.     (*t->move) (xm, y1m);
  2322.     (*t->vector) (xm, y2m);
  2323.     *xl = xm;
  2324.     *yl = y2m;
  2325.  
  2326.     return;
  2327. }
  2328. /* CAC 
  2329.  * Draw horizontal line for the histeps routine.
  2330.  * Performs clipping.
  2331.  */
  2332. static void histeps_horizontal(xl, yl, x1, x2, y)
  2333. int *xl, *yl;            /* keeps track of "cursor" position */
  2334. double x1, x2, y;        /* coordinates of vertical line */
  2335. {
  2336.     struct termentry *t = term;
  2337.     /* global x_min, x_max, y_min, y_max */
  2338.     int x1m, x2m, ym;
  2339.  
  2340.     if ((x1 < x_min && x2 < x_min) ||
  2341.     (x1 > x_max && x2 > x_max) ||
  2342.     y < y_min ||
  2343.     y > y_max)
  2344.     return;
  2345.  
  2346.     if (x1 < x_min)
  2347.     x1 = x_min;
  2348.     if (x1 > x_max)
  2349.     x1 = x_max;
  2350.     if (x2 < x_min)
  2351.     x2 = x_min;
  2352.     if (x2 > x_max)
  2353.     x2 = x_max;
  2354.  
  2355.     ym = map_y(y);
  2356.     x1m = map_x(x1);
  2357.     x2m = map_x(x2);
  2358.  
  2359.     if (x1m != *xl || ym != *yl)
  2360.     (*t->move) (x1m, ym);
  2361.     (*t->vector) (x2m, ym);
  2362.     *xl = x2m;
  2363.     *yl = ym;
  2364.  
  2365.     return;
  2366. }
  2367.  
  2368.  
  2369. /* plot_bars:
  2370.  * Plot the curves in ERRORBARS style
  2371.  *  we just plot the bars; the points are plotted in plot_points
  2372.  */
  2373. static void plot_bars(plot)
  2374. struct curve_points *plot;
  2375. {
  2376.     int i;            /* point index */
  2377.     struct termentry *t = term;
  2378.     double x, y;        /* position of the bar */
  2379.     double ylow, yhigh;        /* the ends of the bars */
  2380.     double xlow, xhigh;
  2381.     double x1, y1, x2, y2, slope;    /* parameters for polar error bars */
  2382.     unsigned int xM, ylowM, yhighM;    /* the mapped version of above */
  2383.     unsigned int yM, xlowM, xhighM;
  2384.     TBOOLEAN low_inrange, high_inrange;
  2385.     int tic = ERRORBARTIC;
  2386.  
  2387. /* Limitation: no boxes with x errorbars */
  2388.  
  2389.     if ((plot->plot_style == YERRORBARS) || (plot->plot_style == XYERRORBARS) ||
  2390.     (plot->plot_style == BOXERROR)) {
  2391. /* Draw the vertical part of the bar */
  2392.     for (i = 0; i < plot->p_count; i++) {
  2393.         /* undefined points don't count */
  2394.         if (plot->points[i].type == UNDEFINED)
  2395.         continue;
  2396.  
  2397.         /* check to see if in xrange */
  2398.         x = plot->points[i].x;
  2399.         if (!inrange(x, x_min, x_max))
  2400.         continue;
  2401.         xM = map_x(x);
  2402.  
  2403.         /* check to see if in yrange */
  2404.         y = plot->points[i].y;
  2405.         if (!inrange(y, y_min, y_max))
  2406.         continue;
  2407.         yM = map_y(y);
  2408.  
  2409.         /* find low and high points of bar, and check yrange */
  2410.         yhigh = plot->points[i].yhigh;
  2411.         ylow = plot->points[i].ylow;
  2412.  
  2413.         high_inrange = inrange(yhigh, y_min, y_max);
  2414.         low_inrange = inrange(ylow, y_min, y_max);
  2415.  
  2416.         /* compute the plot position of yhigh */
  2417.         if (high_inrange)
  2418.         yhighM = map_y(yhigh);
  2419.         else if (samesign(yhigh - y_max, y_max - y_min))
  2420.         yhighM = map_y(y_max);
  2421.         else
  2422.         yhighM = map_y(y_min);
  2423.  
  2424.         /* compute the plot position of ylow */
  2425.         if (low_inrange)
  2426.         ylowM = map_y(ylow);
  2427.         else if (samesign(ylow - y_max, y_max - y_min))
  2428.         ylowM = map_y(y_max);
  2429.         else
  2430.         ylowM = map_y(y_min);
  2431.  
  2432.         if (!high_inrange && !low_inrange && ylowM == yhighM)
  2433.         /* both out of range on the same side */
  2434.         continue;
  2435.  
  2436.         /* find low and high points of bar, and check xrange */
  2437.         xhigh = plot->points[i].xhigh;
  2438.         xlow = plot->points[i].xlow;
  2439.  
  2440.         high_inrange = inrange(xhigh, x_min, x_max);
  2441.         low_inrange = inrange(xlow, x_min, x_max);
  2442.  
  2443.         /* compute the plot position of xhigh */
  2444.         if (high_inrange)
  2445.         xhighM = map_x(xhigh);
  2446.         else if (samesign(xhigh - x_max, x_max - x_min))
  2447.         xhighM = map_x(x_max);
  2448.         else
  2449.         xhighM = map_x(x_min);
  2450.  
  2451.         /* compute the plot position of xlow */
  2452.         if (low_inrange)
  2453.         xlowM = map_x(xlow);
  2454.         else if (samesign(xlow - x_max, x_max - x_min))
  2455.         xlowM = map_x(x_max);
  2456.         else
  2457.         xlowM = map_x(x_min);
  2458.  
  2459.         if (!high_inrange && !low_inrange && xlowM == xhighM)
  2460.         /* both out of range on the same side */
  2461.         continue;
  2462.  
  2463.         /* by here everything has been mapped */
  2464.         if (!polar) {
  2465.         /* HBB 981130: use Igor's routine *only* for polar errorbars */
  2466.         (*t->move) (xM, ylowM);
  2467.         /* draw the main bar */
  2468.         (*t->vector) (xM, yhighM);
  2469.         if (bar_size > 0.0) {
  2470.             /* draw the bottom tic */
  2471.             (*t->move) ((unsigned int) (xM - bar_size * tic), ylowM);
  2472.             (*t->vector) ((unsigned int) (xM + bar_size * tic), ylowM);
  2473.             /* draw the top tic */
  2474.             (*t->move) ((unsigned int) (xM - bar_size * tic), yhighM);
  2475.             (*t->vector) ((unsigned int) (xM + bar_size * tic), yhighM);
  2476.         }
  2477.         } else {
  2478.         /* HBB 981130: see above */
  2479.         /* The above has been replaced by Igor inorder to get errorbars
  2480.            coming out in polar mode AND to stop the bar from going
  2481.            through the symbol */
  2482.         if ((xhighM - xlowM) * (xhighM - xlowM) + (yhighM - ylowM) * (yhighM - ylowM)
  2483.             > pointsize * tic * pointsize * tic * 4.5) {
  2484.             /* Only plot the error bar if it is bigger than the
  2485.              * symbol */
  2486.             /* The factor of 4.5 should strictly be 4.0, but it looks
  2487.              * better to drop the error bar if it is only slightly
  2488.              * bigger than the symbol, Igor. */
  2489.             if (xlowM == xhighM) {
  2490.             (*t->move) (xM, ylowM);
  2491.             /* draw the main bar to the symbol end */
  2492.             (*t->vector) (xM, (unsigned int) (yM - pointsize * tic));
  2493.             (*t->move) (xM, (unsigned int) (yM + pointsize * tic));
  2494.             /* draw the other part of the main bar */
  2495.             (*t->vector) (xM, yhighM);
  2496.             } else {
  2497.             (*t->move) (xlowM, ylowM);
  2498.             /* draw the main bar in polar mode. Note that here
  2499.              * the bar is drawn through the symbol. I tried to
  2500.              * fix this, but got into trouble with the two bars
  2501.              * (on either side of symbol) not being perfectly
  2502.              * parallel due to mapping considerations. Igor
  2503.              */
  2504.             (*t->vector) (xhighM, yhighM);
  2505.             }
  2506.             if (bar_size > 0.0) {
  2507.             /* The following attempts to ensure that the tics
  2508.              * are perpendicular to the error bar, Igor. */
  2509.             /*perpendicular to the main bar */
  2510.             slope = (xlowM * 1.0 - xhighM * 1.0) / (yhighM * 1.0 - ylowM * 1.0 + 1e-10);
  2511.             x1 = xlowM + bar_size * tic / sqrt(1.0 + slope * slope);
  2512.             x2 = xlowM - bar_size * tic / sqrt(1.0 + slope * slope);
  2513.             y1 = slope * (x1 - xlowM) + ylowM;
  2514.             y2 = slope * (x2 - xlowM) + ylowM;
  2515.  
  2516.             /* draw the bottom tic */
  2517.             (*t->move) ((unsigned int) x1, (unsigned int) y1);
  2518.             (*t->vector) ((unsigned int) x2, (unsigned int) y2);
  2519.  
  2520.             x1 = xhighM + bar_size * tic / sqrt(1.0 + slope * slope);
  2521.             x2 = xhighM - bar_size * tic / sqrt(1.0 + slope * slope);
  2522.             y1 = slope * (x1 - xhighM) + yhighM;
  2523.             y2 = slope * (x2 - xhighM) + yhighM;
  2524.             /* draw the top tic */
  2525.             (*t->move) ((unsigned int) x1, (unsigned int) y1);
  2526.             (*t->vector) ((unsigned int) x2, (unsigned int) y2);
  2527.             }        /* if error bar is bigger than symbol */
  2528.         }
  2529.         }            /* HBB 981130: see above */
  2530.     }            /* for loop */
  2531.     }                /* if yerrorbars OR xyerrorbars */
  2532.     if ((plot->plot_style == XERRORBARS) || (plot->plot_style == XYERRORBARS)) {
  2533.  
  2534. /* Draw the horizontal part of the bar */
  2535.     for (i = 0; i < plot->p_count; i++) {
  2536.         /* undefined points don't count */
  2537.         if (plot->points[i].type == UNDEFINED)
  2538.         continue;
  2539.  
  2540.         /* check to see if in yrange */
  2541.         y = plot->points[i].y;
  2542.         if (!inrange(y, y_min, y_max))
  2543.         continue;
  2544.         yM = map_y(y);
  2545.  
  2546.         /* find low and high points of bar, and check xrange */
  2547.         xhigh = plot->points[i].xhigh;
  2548.         xlow = plot->points[i].xlow;
  2549.  
  2550.         high_inrange = inrange(xhigh, x_min, x_max);
  2551.         low_inrange = inrange(xlow, x_min, x_max);
  2552.  
  2553.         /* compute the plot position of xhigh */
  2554.         if (high_inrange)
  2555.         xhighM = map_x(xhigh);
  2556.         else if (samesign(xhigh - x_max, x_max - x_min))
  2557.         xhighM = map_x(x_max);
  2558.         else
  2559.         xhighM = map_x(x_min);
  2560.  
  2561.         /* compute the plot position of xlow */
  2562.         if (low_inrange)
  2563.         xlowM = map_x(xlow);
  2564.         else if (samesign(xlow - x_max, x_max - x_min))
  2565.         xlowM = map_x(x_max);
  2566.         else
  2567.         xlowM = map_x(x_min);
  2568.  
  2569.         if (!high_inrange && !low_inrange && xlowM == xhighM)
  2570.         /* both out of range on the same side */
  2571.         continue;
  2572.  
  2573.         /* by here everything has been mapped */
  2574.         (*t->move) (xlowM, yM);
  2575.         (*t->vector) (xhighM, yM);    /* draw the main bar */
  2576.         if (bar_size > 0.0) {
  2577.         (*t->move) (xlowM, (unsigned int) (yM - bar_size * tic));    /* draw the left tic */
  2578.         (*t->vector) (xlowM, (unsigned int) (yM + bar_size * tic));
  2579.         (*t->move) (xhighM, (unsigned int) (yM - bar_size * tic));    /* draw the right tic */
  2580.         (*t->vector) (xhighM, (unsigned int) (yM + bar_size * tic));
  2581.         }
  2582.     }            /* for loop */
  2583.     }                /* if xerrorbars OR xyerrorbars */
  2584. }
  2585.  
  2586. /* plot_boxes:
  2587.  * Plot the curves in BOXES style
  2588.  */
  2589. static void plot_boxes(plot, xaxis_y)
  2590. struct curve_points *plot;
  2591. int xaxis_y;
  2592. {
  2593.     int i;            /* point index */
  2594.     int xl, xr, yt;        /* point in terminal coordinates */
  2595.     double dxl, dxr, dyt;
  2596.     struct termentry *t = term;
  2597.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  2598.     TBOOLEAN boxxy = (plot->plot_style == BOXXYERROR);
  2599.  
  2600.     for (i = 0; i < plot->p_count; i++) {
  2601.  
  2602.     switch (plot->points[i].type) {
  2603.     case OUTRANGE:
  2604.     case INRANGE:{
  2605.         if (plot->points[i].z < 0.0) {
  2606.             /* need to auto-calc width */
  2607.             /* ASSERT(boxwidth <= 0.0); - else graphics.c
  2608.              * provides width */
  2609.  
  2610.             /* calculate width */
  2611.             if (prev != UNDEFINED)
  2612.             dxl = (plot->points[i - 1].x - plot->points[i].x) / 2.0;
  2613.             else
  2614.             dxl = 0.0;
  2615.  
  2616.             if (i < plot->p_count - 1) {
  2617.             if (plot->points[i + 1].type != UNDEFINED)
  2618.                 dxr = (plot->points[i + 1].x - plot->points[i].x) / 2.0;
  2619.             else
  2620.                 dxr = -dxl;
  2621.             } else {
  2622.             dxr = -dxl;
  2623.             }
  2624.  
  2625.             if (prev == UNDEFINED)
  2626.             dxl = -dxr;
  2627.  
  2628.             dxl = plot->points[i].x + dxl;
  2629.             dxr = plot->points[i].x + dxr;
  2630.         } else {    /* z >= 0 */
  2631.             dxr = plot->points[i].xhigh;
  2632.             dxl = plot->points[i].xlow;
  2633.         }
  2634.  
  2635.         if (boxxy) {
  2636.             dyt = plot->points[i].yhigh;
  2637.             xaxis_y = map_y(plot->points[i].ylow);
  2638.         } else {
  2639.             dyt = plot->points[i].y;
  2640.         }
  2641.  
  2642.         /* clip to border */
  2643.         if ((y_min < y_max && dyt < y_min)
  2644.             || (y_max < y_min && dyt > y_min))
  2645.             dyt = y_min;
  2646.         if ((y_min < y_max && dyt > y_max)
  2647.             || (y_max < y_min && dyt < y_max))
  2648.             dyt = y_max;
  2649.         if ((x_min < x_max && dxr < x_min)
  2650.             || (x_max < x_min && dxr > x_min))
  2651.             dxr = x_min;
  2652.         if ((x_min < x_max && dxr > x_max)
  2653.             || (x_max < x_min && dxr < x_max))
  2654.             dxr = x_max;
  2655.         if ((x_min < x_max && dxl < x_min)
  2656.             || (x_max < x_min && dxl > x_min))
  2657.             dxl = x_min;
  2658.         if ((x_min < x_max && dxl > x_max)
  2659.             || (x_max < x_min && dxl < x_max))
  2660.             dxl = x_max;
  2661.  
  2662.         xl = map_x(dxl);
  2663.         xr = map_x(dxr);
  2664.         yt = map_y(dyt);
  2665.  
  2666.         (*t->move) (xl, xaxis_y);
  2667.         (*t->vector) (xl, yt);
  2668.         (*t->vector) (xr, yt);
  2669.         (*t->vector) (xr, xaxis_y);
  2670.         (*t->vector) (xl, xaxis_y);
  2671.         break;
  2672.         }            /* case OUTRANGE, INRANGE */
  2673.  
  2674.     default:        /* just a safety */
  2675.     case UNDEFINED:{
  2676.         break;
  2677.         }
  2678.  
  2679.     }            /* switch point-type */
  2680.  
  2681.     prev = plot->points[i].type;
  2682.  
  2683.     }                /*loop */
  2684. }
  2685.  
  2686.  
  2687.  
  2688. /* plot_points:
  2689.  * Plot the curves in POINTSTYLE style
  2690.  */
  2691. static void plot_points(plot)
  2692. struct curve_points *plot;
  2693. {
  2694.     int i;
  2695.     int x, y;
  2696.     struct termentry *t = term;
  2697.  
  2698.     for (i = 0; i < plot->p_count; i++) {
  2699.     if (plot->points[i].type == INRANGE) {
  2700.         x = map_x(plot->points[i].x);
  2701.         y = map_y(plot->points[i].y);
  2702.         /* do clipping if necessary */
  2703.         if (!clip_points ||
  2704.         (x >= xleft + p_width && y >= ybot + p_height
  2705.          && x <= xright - p_width && y <= ytop - p_height))
  2706.         (*t->point) (x, y, plot->lp_properties.p_type);
  2707.     }
  2708.     }
  2709. }
  2710.  
  2711. /* plot_dots:
  2712.  * Plot the curves in DOTS style
  2713.  */
  2714. static void plot_dots(plot)
  2715. struct curve_points *plot;
  2716. {
  2717.     int i;
  2718.     int x, y;
  2719.     struct termentry *t = term;
  2720.  
  2721.     for (i = 0; i < plot->p_count; i++) {
  2722.     if (plot->points[i].type == INRANGE) {
  2723.         x = map_x(plot->points[i].x);
  2724.         y = map_y(plot->points[i].y);
  2725.         /* point type -1 is a dot */
  2726.         (*t->point) (x, y, -1);
  2727.     }
  2728.     }
  2729. }
  2730.  
  2731. /* plot_vectors:
  2732.  * Plot the curves in VECTORS style
  2733.  */
  2734. static void plot_vectors(plot)
  2735. struct curve_points *plot;
  2736. {
  2737.     int i;
  2738.     int x1, y1, x2, y2;
  2739.     struct termentry *t = term;
  2740.  
  2741.     for (i = 0; i < plot->p_count; i++) {
  2742.     if (plot->points[i].type == INRANGE) {
  2743.         x1 = map_x(plot->points[i].xlow);
  2744.         y1 = map_y(plot->points[i].ylow);
  2745.         x2 = map_x(plot->points[i].xhigh);
  2746.         y2 = map_y(plot->points[i].yhigh);
  2747.         (*t->arrow) (x1, y1, x2, y2, TRUE);
  2748.     }
  2749.     }
  2750. }
  2751.  
  2752.  
  2753. /* plot_f_bars() - finance bars */
  2754. static void plot_f_bars(plot)
  2755. struct curve_points *plot;
  2756. {
  2757.     int i;            /* point index */
  2758.     struct termentry *t = term;
  2759.     double x;            /* position of the bar */
  2760.     double ylow, yhigh, yclose, yopen;    /* the ends of the bars */
  2761.     unsigned int xM, ylowM, yhighM;    /* the mapped version of above */
  2762.     TBOOLEAN low_inrange, high_inrange;
  2763.     int tic = ERRORBARTIC / 2;
  2764.  
  2765.     for (i = 0; i < plot->p_count; i++) {
  2766.     /* undefined points don't count */
  2767.     if (plot->points[i].type == UNDEFINED)
  2768.         continue;
  2769.  
  2770.     /* check to see if in xrange */
  2771.     x = plot->points[i].x;
  2772.     if (!inrange(x, x_min, x_max))
  2773.         continue;
  2774.     xM = map_x(x);
  2775.  
  2776.     /* find low and high points of bar, and check yrange */
  2777.     yhigh = plot->points[i].yhigh;
  2778.     ylow = plot->points[i].ylow;
  2779.     yclose = plot->points[i].z;
  2780.     yopen = plot->points[i].y;
  2781.  
  2782.     high_inrange = inrange(yhigh, y_min, y_max);
  2783.     low_inrange = inrange(ylow, y_min, y_max);
  2784.  
  2785.     /* compute the plot position of yhigh */
  2786.     if (high_inrange)
  2787.         yhighM = map_y(yhigh);
  2788.     else if (samesign(yhigh - y_max, y_max - y_min))
  2789.         yhighM = map_y(y_max);
  2790.     else
  2791.         yhighM = map_y(y_min);
  2792.  
  2793.     /* compute the plot position of ylow */
  2794.     if (low_inrange)
  2795.         ylowM = map_y(ylow);
  2796.     else if (samesign(ylow - y_max, y_max - y_min))
  2797.         ylowM = map_y(y_max);
  2798.     else
  2799.         ylowM = map_y(y_min);
  2800.  
  2801.     if (!high_inrange && !low_inrange && ylowM == yhighM)
  2802.         /* both out of range on the same side */
  2803.         continue;
  2804.  
  2805.     /* by here everything has been mapped */
  2806.     (*t->move) (xM, ylowM);
  2807.     (*t->vector) (xM, yhighM);    /* draw the main bar */
  2808.     /* draw the open tic */
  2809.     (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2810.     (*t->vector) (xM, map_y(yopen));
  2811.     /* draw the close tic */
  2812.     (*t->move) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
  2813.     (*t->vector) (xM, map_y(yclose));
  2814.     }
  2815. }
  2816.  
  2817.  
  2818. /* plot_c_bars:
  2819.  * Plot the curves in CANDLESTICSK style
  2820.  *  we just plot the bars; the points are not plotted 
  2821.  */
  2822. static void plot_c_bars(plot)
  2823. struct curve_points *plot;
  2824. {
  2825.     int i;            /* point index */
  2826.     struct termentry *t = term;
  2827.     double x;            /* position of the bar */
  2828.     double ylow, yhigh, yclose, yopen;    /* the ends of the bars */
  2829.     unsigned int xM, ylowM, yhighM;    /* the mapped version of above */
  2830.     TBOOLEAN low_inrange, high_inrange;
  2831.     int tic = ERRORBARTIC / 2;
  2832.  
  2833.     for (i = 0; i < plot->p_count; i++) {
  2834.     /* undefined points don't count */
  2835.     if (plot->points[i].type == UNDEFINED)
  2836.         continue;
  2837.  
  2838.     /* check to see if in xrange */
  2839.     x = plot->points[i].x;
  2840.     if (!inrange(x, x_min, x_max))
  2841.         continue;
  2842.     xM = map_x(x);
  2843.  
  2844.     /* find low and high points of bar, and check yrange */
  2845.     yhigh = plot->points[i].yhigh;
  2846.     ylow = plot->points[i].ylow;
  2847.     yclose = plot->points[i].z;
  2848.     yopen = plot->points[i].y;
  2849.  
  2850.     high_inrange = inrange(yhigh, y_min, y_max);
  2851.     low_inrange = inrange(ylow, y_min, y_max);
  2852.  
  2853.     /* compute the plot position of yhigh */
  2854.     if (high_inrange)
  2855.         yhighM = map_y(yhigh);
  2856.     else if (samesign(yhigh - y_max, y_max - y_min))
  2857.         yhighM = map_y(y_max);
  2858.     else
  2859.         yhighM = map_y(y_min);
  2860.  
  2861.     /* compute the plot position of ylow */
  2862.     if (low_inrange)
  2863.         ylowM = map_y(ylow);
  2864.     else if (samesign(ylow - y_max, y_max - y_min))
  2865.         ylowM = map_y(y_max);
  2866.     else
  2867.         ylowM = map_y(y_min);
  2868.  
  2869.     if (!high_inrange && !low_inrange && ylowM == yhighM)
  2870.         /* both out of range on the same side */
  2871.         continue;
  2872.  
  2873.     /* by here everything has been mapped */
  2874.     if (yopen <= yclose) {
  2875.         (*t->move) (xM, ylowM);
  2876.         (*t->vector) (xM, map_y(yopen));    /* draw the lower bar */
  2877.         /* draw the open tic */
  2878.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2879.         /* draw the open tic */
  2880.         (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yopen));
  2881.         (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
  2882.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yclose));
  2883.         /* draw the open tic */
  2884.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2885.         (*t->move) (xM, map_y(yclose));    /* draw the close tic */
  2886.         (*t->vector) (xM, yhighM);
  2887.     } else {
  2888.         (*t->move) (xM, ylowM);
  2889.         (*t->vector) (xM, yhighM);
  2890.         /* draw the open tic */
  2891.         (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2892.         /* draw the open tic */
  2893.         (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yopen));
  2894.         (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
  2895.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yclose));
  2896.         /* draw the open tic */
  2897.         (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
  2898.         /* draw the close tic */
  2899.         (*t->move) ((unsigned int) (xM - bar_size * tic / 2), map_y(yclose));
  2900.         /* draw the open tic */
  2901.         (*t->vector) ((unsigned int) (xM - bar_size * tic / 2), map_y(yopen));
  2902.         /* draw the close tic */
  2903.         (*t->move) ((unsigned int) (xM + bar_size * tic / 2), map_y(yclose));
  2904.         /* draw the open tic */
  2905.         (*t->vector) ((unsigned int) (xM + bar_size * tic / 2), map_y(yopen));
  2906.     }
  2907.  
  2908.     }
  2909. }
  2910.  
  2911. /* single edge intersection algorithm */
  2912. /* Given two points, one inside and one outside the plot, return
  2913.  * the point where an edge of the plot intersects the line segment defined 
  2914.  * by the two points.
  2915.  */
  2916. static void edge_intersect(points, i, ex, ey)
  2917. struct coordinate GPHUGE *points;    /* the points array */
  2918. int i;                /* line segment from point i-1 to point i */
  2919. double *ex, *ey;        /* the point where it crosses an edge */
  2920. {
  2921.     /* global x_min, x_max, y_min, x_max */
  2922.     double ix = points[i - 1].x;
  2923.     double iy = points[i - 1].y;
  2924.     double ox = points[i].x;
  2925.     double oy = points[i].y;
  2926.     double x, y;        /* possible intersection point */
  2927.  
  2928.     if (points[i].type == INRANGE) {
  2929.     /* swap points around so that ix/ix/iz are INRANGE and
  2930.      * ox/oy/oz are OUTRANGE
  2931.      */
  2932.     x = ix;
  2933.     ix = ox;
  2934.     ox = x;
  2935.     y = iy;
  2936.     iy = oy;
  2937.     oy = y;
  2938.     }
  2939.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  2940.      * cope with them here, so don't process them as a "real" OUTRANGE point 
  2941.      *
  2942.      * If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  2943.      * so drop out by returning the INRANGE point. 
  2944.      *
  2945.      * Obviously, only need to test the OUTRANGE point (coordinates) */
  2946.     if (ox == -VERYLARGE || oy == -VERYLARGE) {
  2947.     *ex = ix;
  2948.     *ey = iy;
  2949.  
  2950.     if (ox == -VERYLARGE) {
  2951.         /* can't get a direction to draw line, so simply
  2952.          * return INRANGE point */
  2953.         if (oy == -VERYLARGE)
  2954.         return;
  2955.  
  2956.         *ex = x_min;
  2957.         return;
  2958.     }
  2959.     /* obviously oy is -VERYLARGE and ox != -VERYLARGE */
  2960.     *ey = y_min;
  2961.     return;
  2962.     }
  2963.     /*
  2964.      * Can't have case (ix == ox && iy == oy) as one point
  2965.      * is INRANGE and one point is OUTRANGE.
  2966.      */
  2967.     if (iy == oy) {
  2968.     /* horizontal line */
  2969.     /* assume inrange(iy, y_min, y_max) */
  2970.     *ey = iy;        /* == oy */
  2971.  
  2972.     if (inrange(x_max, ix, ox))
  2973.         *ex = x_max;
  2974.     else if (inrange(x_min, ix, ox))
  2975.         *ex = x_min;
  2976.     else {
  2977.         graph_error("error in edge_intersect");
  2978.     }
  2979.     return;
  2980.     } else if (ix == ox) {
  2981.     /* vertical line */
  2982.     /* assume inrange(ix, x_min, x_max) */
  2983.     *ex = ix;        /* == ox */
  2984.  
  2985.     if (inrange(y_max, iy, oy))
  2986.         *ey = y_max;
  2987.     else if (inrange(y_min, iy, oy))
  2988.         *ey = y_min;
  2989.     else {
  2990.         graph_error("error in edge_intersect");
  2991.     }
  2992.     return;
  2993.     }
  2994.     /* slanted line of some kind */
  2995.  
  2996.     /* does it intersect y_min edge */
  2997.     if (inrange(y_min, iy, oy) && y_min != iy && y_min != oy) {
  2998.     x = ix + (y_min - iy) * ((ox - ix) / (oy - iy));
  2999.     if (inrange(x, x_min, x_max)) {
  3000.         *ex = x;
  3001.         *ey = y_min;
  3002.         return;        /* yes */
  3003.     }
  3004.     }
  3005.     /* does it intersect y_max edge */
  3006.     if (inrange(y_max, iy, oy) && y_max != iy && y_max != oy) {
  3007.     x = ix + (y_max - iy) * ((ox - ix) / (oy - iy));
  3008.     if (inrange(x, x_min, x_max)) {
  3009.         *ex = x;
  3010.         *ey = y_max;
  3011.         return;        /* yes */
  3012.     }
  3013.     }
  3014.     /* does it intersect x_min edge */
  3015.     if (inrange(x_min, ix, ox) && x_min != ix && x_min != ox) {
  3016.     y = iy + (x_min - ix) * ((oy - iy) / (ox - ix));
  3017.     if (inrange(y, y_min, y_max)) {
  3018.         *ex = x_min;
  3019.         *ey = y;
  3020.         return;
  3021.     }
  3022.     }
  3023.     /* does it intersect x_max edge */
  3024.     if (inrange(x_max, ix, ox) && x_max != ix && x_max != ox) {
  3025.     y = iy + (x_max - ix) * ((oy - iy) / (ox - ix));
  3026.     if (inrange(y, y_min, y_max)) {
  3027.         *ex = x_max;
  3028.         *ey = y;
  3029.         return;
  3030.     }
  3031.     }
  3032.     /* If we reach here, the inrange point is on the edge, and
  3033.      * the line segment from the outrange point does not cross any 
  3034.      * other edges to get there. In this case, we return the inrange 
  3035.      * point as the 'edge' intersection point. This will basically draw
  3036.      * line.
  3037.      */
  3038.     *ex = ix;
  3039.     *ey = iy;
  3040.     return;
  3041. }
  3042.  
  3043. /* XXX - JG  */
  3044. /* single edge intersection algorithm for "steps" curves */
  3045. /* 
  3046.  * Given two points, one inside and one outside the plot, return
  3047.  * the point where an edge of the plot intersects the line segments
  3048.  * forming the step between the two points. 
  3049.  *
  3050.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  3051.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and 
  3052.  * (x2,y1)->(x2,y2). 
  3053.  */
  3054. static void edge_intersect_steps(points, i, ex, ey)
  3055. struct coordinate GPHUGE *points;    /* the points array */
  3056. int i;                /* line segment from point i-1 to point i */
  3057. double *ex, *ey;        /* the point where it crosses an edge */
  3058. {
  3059.     /* global x_min, x_max, y_min, x_max */
  3060.     double ax = points[i - 1].x;
  3061.     double ay = points[i - 1].y;
  3062.     double bx = points[i].x;
  3063.     double by = points[i].y;
  3064.  
  3065.     if (points[i].type == INRANGE) {    /* from OUTRANGE to INRANG */
  3066.     if (inrange(ay, y_min, y_max)) {
  3067.         *ey = ay;
  3068.         if (ax > x_max)
  3069.         *ex = x_max;
  3070.         else        /* x < x_min */
  3071.         *ex = x_min;
  3072.     } else {
  3073.         *ex = bx;
  3074.         if (ay > y_max)
  3075.         *ey = y_max;
  3076.         else        /* y < y_min */
  3077.         *ey = y_min;
  3078.     }
  3079.     } else {            /* from INRANGE to OUTRANGE */
  3080.     if (inrange(bx, x_min, x_max)) {
  3081.         *ex = bx;
  3082.         if (by > y_max)
  3083.         *ey = y_max;
  3084.         else        /* y < y_min */
  3085.         *ey = y_min;
  3086.     } else {
  3087.         *ey = ay;
  3088.         if (bx > x_max)
  3089.         *ex = x_max;
  3090.         else        /* x < x_min */
  3091.         *ex = x_min;
  3092.     }
  3093.     }
  3094.     return;
  3095. }
  3096.  
  3097. /* XXX - HOE  */
  3098. /* single edge intersection algorithm for "fsteps" curves */
  3099. /* fsteps means step on forward y-value. 
  3100.  * Given two points, one inside and one outside the plot, return
  3101.  * the point where an edge of the plot intersects the line segments
  3102.  * forming the step between the two points. 
  3103.  *
  3104.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  3105.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and 
  3106.  * (x1,y2)->(x2,y2). 
  3107.  */
  3108. static void edge_intersect_fsteps(points, i, ex, ey)
  3109. struct coordinate GPHUGE *points;    /* the points array */
  3110. int i;                /* line segment from point i-1 to point i */
  3111. double *ex, *ey;        /* the point where it crosses an edge */
  3112. {
  3113.     /* global x_min, x_max, y_min, x_max */
  3114.     double ax = points[i - 1].x;
  3115.     double ay = points[i - 1].y;
  3116.     double bx = points[i].x;
  3117.     double by = points[i].y;
  3118.  
  3119.     if (points[i].type == INRANGE) {    /* from OUTRANGE to INRANG */
  3120.     if (inrange(ax, x_min, x_max)) {
  3121.         *ex = ax;
  3122.         if (ay > y_max)
  3123.         *ey = y_max;
  3124.         else        /* y < y_min */
  3125.         *ey = y_min;
  3126.     } else {
  3127.         *ey = by;
  3128.         if (bx > x_max)
  3129.         *ex = x_max;
  3130.         else        /* x < x_min */
  3131.         *ex = x_min;
  3132.     }
  3133.     } else {            /* from INRANGE to OUTRANGE */
  3134.     if (inrange(by, y_min, y_max)) {
  3135.         *ey = by;
  3136.         if (bx > x_max)
  3137.         *ex = x_max;
  3138.         else        /* x < x_min */
  3139.         *ex = x_min;
  3140.     } else {
  3141.         *ex = ax;
  3142.         if (by > y_max)
  3143.         *ey = y_max;
  3144.         else        /* y < y_min */
  3145.         *ey = y_min;
  3146.     }
  3147.     }
  3148.     return;
  3149. }
  3150.  
  3151. /* XXX - JG  */
  3152. /* double edge intersection algorithm for "steps" plot */
  3153. /* Given two points, both outside the plot, return the points where an 
  3154.  * edge of the plot intersects the line segments forming a step 
  3155.  * by the two points. There may be zero, one, two, or an infinite number
  3156.  * of intersection points. (One means an intersection at a corner, infinite
  3157.  * means overlaying the edge itself). We return FALSE when there is nothing
  3158.  * to draw (zero intersections), and TRUE when there is something to 
  3159.  * draw (the one-point case is a degenerate of the two-point case and we do 
  3160.  * not distinguish it - we draw it anyway).
  3161.  *
  3162.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  3163.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and 
  3164.  * (x2,y1)->(x2,y2). 
  3165.  */
  3166. static TBOOLEAN /* any intersection? */
  3167.  two_edge_intersect_steps(points, i, lx, ly)
  3168. struct coordinate GPHUGE *points;    /* the points array */
  3169. int i;                /* line segment from point i-1 to point i */
  3170. double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  3171. {
  3172.     /* global x_min, x_max, y_min, x_max */
  3173.     double ax = points[i - 1].x;
  3174.     double ay = points[i - 1].y;
  3175.     double bx = points[i].x;
  3176.     double by = points[i].y;
  3177.  
  3178.     if (GPMAX(ax, bx) < x_min || GPMIN(ax, bx) > x_max ||
  3179.     GPMAX(ay, by) < y_min || GPMIN(ay, by) > y_max ||
  3180.     ((ay > y_max || ay < y_min) &&
  3181.      (bx > x_max || bx < x_min))) {
  3182.     return (FALSE);
  3183.     } else if (inrange(ay, y_min, y_max) && inrange(bx, x_min, x_max)) {    /* corner of step inside plotspace */
  3184.     *ly++ = ay;
  3185.     if (ax < x_min)
  3186.         *lx++ = x_min;
  3187.     else
  3188.         *lx++ = x_max;
  3189.  
  3190.     *lx++ = bx;
  3191.     if (by < y_min)
  3192.         *ly++ = y_min;
  3193.     else
  3194.         *ly++ = y_max;
  3195.  
  3196.     return (TRUE);
  3197.     } else if (inrange(ay, y_min, y_max)) {    /* cross plotspace in x-direction */
  3198.     *lx++ = x_min;
  3199.     *ly++ = ay;
  3200.     *lx++ = x_max;
  3201.     *ly++ = ay;
  3202.     return (TRUE);
  3203.     } else if (inrange(ax, x_min, x_max)) {    /* cross plotspace in y-direction */
  3204.     *lx++ = bx;
  3205.     *ly++ = y_min;
  3206.     *lx++ = bx;
  3207.     *ly++ = y_max;
  3208.     return (TRUE);
  3209.     } else
  3210.     return (FALSE);
  3211. }
  3212.  
  3213. /* XXX - HOE  */
  3214. /* double edge intersection algorithm for "fsteps" plot */
  3215. /* Given two points, both outside the plot, return~Le an 
  3216.  * edge of the plot intersects the line segments forming a step 
  3217.  * by the two points. There may be zero, one, two, or an infinite number
  3218.  * of intersection points. (One means an intersection at a corner, infinite
  3219.  * means overlaying the edge itself). We return FALSE when there is nothing
  3220.  * to draw (zero intersections), and TRUE when there is something to 
  3221.  * draw (the one-point case is a degenerate of the two-point case and we do 
  3222.  * not distinguish it - we draw it anyway).
  3223.  *
  3224.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  3225.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and 
  3226.  * (x1,y2)->(x2,y2). 
  3227.  */
  3228. static TBOOLEAN /* any intersection? */
  3229.  two_edge_intersect_fsteps(points, i, lx, ly)
  3230. struct coordinate GPHUGE *points;    /* the points array */
  3231. int i;                /* line segment from point i-1 to point i */
  3232. double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  3233. {
  3234.     /* global x_min, x_max, y_min, x_max */
  3235.     double ax = points[i - 1].x;
  3236.     double ay = points[i - 1].y;
  3237.     double bx = points[i].x;
  3238.     double by = points[i].y;
  3239.  
  3240.     if (GPMAX(ax, bx) < x_min || GPMIN(ax, bx) > x_max ||
  3241.     GPMAX(ay, by) < y_min || GPMIN(ay, by) > y_max ||
  3242.     ((by > y_max || by < y_min) &&
  3243.      (ax > x_max || ax < x_min))) {
  3244.     return (FALSE);
  3245.     } else if (inrange(by, y_min, y_max) && inrange(ax, x_min, x_max)) {    /* corner of step inside plotspace */
  3246.     *lx++ = ax;
  3247.     if (ay < y_min)
  3248.         *ly++ = y_min;
  3249.     else
  3250.         *ly++ = y_max;
  3251.  
  3252.     *ly = by;
  3253.     if (bx < x_min)
  3254.         *lx = x_min;
  3255.     else
  3256.         *lx = x_max;
  3257.  
  3258.     return (TRUE);
  3259.     } else if (inrange(by, y_min, y_max)) {    /* cross plotspace in x-direction */
  3260.     *lx++ = x_min;
  3261.     *ly++ = by;
  3262.     *lx = x_max;
  3263.     *ly = by;
  3264.     return (TRUE);
  3265.     } else if (inrange(ax, x_min, x_max)) {    /* cross plotspace in y-direction */
  3266.     *lx++ = ax;
  3267.     *ly++ = y_min;
  3268.     *lx = ax;
  3269.     *ly = y_max;
  3270.     return (TRUE);
  3271.     } else
  3272.     return (FALSE);
  3273. }
  3274.  
  3275. /* double edge intersection algorithm */
  3276. /* Given two points, both outside the plot, return
  3277.  * the points where an edge of the plot intersects the line segment defined 
  3278.  * by the two points. There may be zero, one, two, or an infinite number
  3279.  * of intersection points. (One means an intersection at a corner, infinite
  3280.  * means overlaying the edge itself). We return FALSE when there is nothing
  3281.  * to draw (zero intersections), and TRUE when there is something to 
  3282.  * draw (the one-point case is a degenerate of the two-point case and we do 
  3283.  * not distinguish it - we draw it anyway).
  3284.  */
  3285. static TBOOLEAN /* any intersection? */
  3286.                                two_edge_intersect(points, i, lx, ly)
  3287. struct coordinate GPHUGE *points;    /* the points array */
  3288. int i;                /* line segment from point i-1 to point i */
  3289. double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  3290. {
  3291.     /* global x_min, x_max, y_min, x_max */
  3292.     int count;
  3293.     double ix = points[i - 1].x;
  3294.     double iy = points[i - 1].y;
  3295.     double ox = points[i].x;
  3296.     double oy = points[i].y;
  3297.     double t[4];
  3298.     double swap;
  3299.     double t_min, t_max;
  3300. #if 0
  3301.     fprintf(stderr, "\ntwo_edge_intersect (%g, %g) and (%g, %g) : ",
  3302.         points[i - 1].x, points[i - 1].y,
  3303.         points[i].x, points[i].y);
  3304. #endif
  3305.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  3306.        cope with them here, so don't process them as a "real" OUTRANGE point 
  3307.  
  3308.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  3309.        so drop out by returning FALSE */
  3310.  
  3311.     count = 0;
  3312.     if (ix == -VERYLARGE)
  3313.     count++;
  3314.     if (ox == -VERYLARGE)
  3315.     count++;
  3316.     if (iy == -VERYLARGE)
  3317.     count++;
  3318.     if (oy == -VERYLARGE)
  3319.     count++;
  3320.  
  3321.     /* either doesn't pass through graph area *or* 
  3322.        can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
  3323.     if (count > 1) {
  3324. #if 0
  3325.     fprintf(stderr, "\tA\n");
  3326. #endif
  3327.     return (FALSE);
  3328.     }
  3329.     if (ox == -VERYLARGE || ix == -VERYLARGE) {
  3330.     if (ix == -VERYLARGE) {
  3331.         /* swap points so ix/iy don't have a -VERYLARGE component */
  3332.         swap = ix;
  3333.         ix = ox;
  3334.         ox = swap;
  3335.         swap = iy;
  3336.         iy = oy;
  3337.         oy = swap;
  3338.     }
  3339.     /* check actually passes through the graph area */
  3340.     if (ix > x_max && inrange(iy, y_min, y_max)) {
  3341.         lx[0] = x_min;
  3342.         ly[0] = iy;
  3343.  
  3344.         lx[1] = x_max;
  3345.         ly[1] = iy;
  3346. #if 0
  3347.         fprintf(stderr, "(%g %g) -> (%g %g)",
  3348.             lx[0], ly[0], lx[1], ly[1]);
  3349. #endif
  3350.         return (TRUE);
  3351.     } else {
  3352. #if 0
  3353.         fprintf(stderr, "\tB\n");
  3354. #endif
  3355.         return (FALSE);
  3356.     }
  3357.     }
  3358.     if (oy == -VERYLARGE || iy == -VERYLARGE) {
  3359.     if (iy == -VERYLARGE) {
  3360.         /* swap points so ix/iy don't have a -VERYLARGE component */
  3361.         swap = ix;
  3362.         ix = ox;
  3363.         ox = swap;
  3364.         swap = iy;
  3365.         iy = oy;
  3366.         oy = swap;
  3367.     }
  3368.     /* check actually passes through the graph area */
  3369.     if (iy > y_max && inrange(ix, x_min, x_max)) {
  3370.         lx[0] = ix;
  3371.         ly[0] = y_min;
  3372.  
  3373.         lx[1] = ix;
  3374.         ly[1] = y_max;
  3375. #if 0
  3376.         fprintf(stderr, "(%g %g) -> (%g %g)",
  3377.             lx[0], ly[0], lx[1], ly[1]);
  3378. #endif
  3379.         return (TRUE);
  3380.     } else {
  3381. #if 0
  3382.         fprintf(stderr, "\tC\n");
  3383. #endif
  3384.         return (FALSE);
  3385.     }
  3386.     }
  3387.     /*
  3388.      * Special horizontal/vertical, etc. cases are checked and remaining
  3389.      * slant lines are checked separately.
  3390.      *
  3391.      * The slant line intersections are solved using the parametric form
  3392.      * of the equation for a line, since if we test x/y min/max planes explicitly
  3393.      * then e.g. a  line passing through a corner point (x_min,y_min) 
  3394.      * actually intersects 2 planes and hence further tests would be required 
  3395.      * to anticipate this and similar situations.
  3396.      */
  3397.  
  3398.     /*
  3399.      * Can have case (ix == ox && iy == oy) as both points OUTRANGE
  3400.      */
  3401.     if (ix == ox && iy == oy) {
  3402.     /* but as only define single outrange point, can't intersect graph area */
  3403.     return (FALSE);
  3404.     }
  3405.     if (ix == ox) {
  3406.     /* line parallel to y axis */
  3407.  
  3408.     /* x coord must be in range, and line must span both y_min and y_max */
  3409.     /* note that spanning y_min implies spanning y_max, as both points OUTRANGE */
  3410.     if (!inrange(ix, x_min, x_max)) {
  3411.         return (FALSE);
  3412.     }
  3413.     if (inrange(y_min, iy, oy)) {
  3414.         lx[0] = ix;
  3415.         ly[0] = y_min;
  3416.  
  3417.         lx[1] = ix;
  3418.         ly[1] = y_max;
  3419. #if 0
  3420.         fprintf(stderr, "(%g %g) -> (%g %g)",
  3421.             lx[0], ly[0], lx[1], ly[1]);
  3422. #endif
  3423.         return (TRUE);
  3424.     } else
  3425.         return (FALSE);
  3426.     }
  3427.     if (iy == oy) {
  3428.     /* already checked case (ix == ox && iy == oy) */
  3429.  
  3430.     /* line parallel to x axis */
  3431.     /* y coord must be in range, and line must span both x_min and x_max */
  3432.     /* note that spanning x_min implies spanning x_max, as both points OUTRANGE */
  3433.     if (!inrange(iy, y_min, y_max)) {
  3434.         return (FALSE);
  3435.     }
  3436.     if (inrange(x_min, ix, ox)) {
  3437.         lx[0] = x_min;
  3438.         ly[0] = iy;
  3439.  
  3440.         lx[1] = x_max;
  3441.         ly[1] = iy;
  3442. #if 0
  3443.         fprintf(stderr, "(%g %g) -> (%g %g)",
  3444.             lx[0], ly[0], lx[1], ly[1]);
  3445. #endif
  3446.         return (TRUE);
  3447.     } else
  3448.         return (FALSE);
  3449.     }
  3450.     /* nasty 2D slanted line in an xy plane */
  3451.  
  3452.     /*
  3453.        Solve parametric equation
  3454.  
  3455.        (ix, iy) + t (diff_x, diff_y)
  3456.  
  3457.        where 0.0 <= t <= 1.0 and
  3458.  
  3459.        diff_x = (ox - ix);
  3460.        diff_y = (oy - iy);
  3461.      */
  3462.  
  3463.     t[0] = (x_min - ix) / (ox - ix);
  3464.     t[1] = (x_max - ix) / (ox - ix);
  3465.  
  3466.     if (t[0] > t[1]) {
  3467.     swap = t[0];
  3468.     t[0] = t[1];
  3469.     t[1] = swap;
  3470.     }
  3471.     t[2] = (y_min - iy) / (oy - iy);
  3472.     t[3] = (y_max - iy) / (oy - iy);
  3473.  
  3474.     if (t[2] > t[3]) {
  3475.     swap = t[2];
  3476.     t[2] = t[3];
  3477.     t[3] = swap;
  3478.     }
  3479.     t_min = GPMAX(GPMAX(t[0], t[2]), 0.0);
  3480.     t_max = GPMIN(GPMIN(t[1], t[3]), 1.0);
  3481.  
  3482.     if (t_min > t_max)
  3483.     return (FALSE);
  3484.  
  3485.     lx[0] = ix + t_min * (ox - ix);
  3486.     ly[0] = iy + t_min * (oy - iy);
  3487.  
  3488.     lx[1] = ix + t_max * (ox - ix);
  3489.     ly[1] = iy + t_max * (oy - iy);
  3490.  
  3491.     /*
  3492.      * Can only have 0 or 2 intersection points -- only need test one coord
  3493.      */
  3494.     if (inrange(lx[0], x_min, x_max) &&
  3495.     inrange(ly[0], y_min, y_max)) {
  3496. #if 0
  3497.     fprintf(stderr, "(%g %g) -> (%g %g)",
  3498.         lx[0], ly[0], lx[1], ly[1]);
  3499. #endif
  3500.     return (TRUE);
  3501.     }
  3502.     return (FALSE);
  3503. }
  3504.  
  3505. /* justify ticplace to a proper date-time value */
  3506. double time_tic_just(level, ticplace)
  3507. int level;
  3508. double ticplace;
  3509. {
  3510.     struct tm tm;
  3511.  
  3512.     if (level <= 0) {
  3513.     return (ticplace);
  3514.     }
  3515.     ggmtime(&tm, (double) ticplace);
  3516.     if (level > 0) {        /* units of minutes */
  3517.     if (tm.tm_sec > 50)
  3518.         tm.tm_min++;
  3519.     tm.tm_sec = 0;
  3520.     }
  3521.     if (level > 1) {        /* units of hours */
  3522.     if (tm.tm_min > 50)
  3523.         tm.tm_hour++;
  3524.     tm.tm_min = 0;
  3525.     }
  3526.     if (level > 2) {        /* units of days */
  3527.     if (tm.tm_hour > 14) {
  3528.         tm.tm_hour = 0;
  3529.         tm.tm_mday = 0;
  3530.         tm.tm_yday++;
  3531.         ggmtime(&tm, (double) gtimegm(&tm));
  3532.     } else if (tm.tm_hour > 7) {
  3533.         tm.tm_hour = 12;
  3534.     } else if (tm.tm_hour > 3) {
  3535.         tm.tm_hour = 6;
  3536.     } else {
  3537.         tm.tm_hour = 0;
  3538.     }
  3539.     }
  3540.     /* skip it, I have not bothered with weekday so far */
  3541.     if (level > 4) {        /* units of month */
  3542.     if (tm.tm_mday > 25) {
  3543.         tm.tm_mon++;
  3544.         if (tm.tm_mon > 11) {
  3545.         tm.tm_year++;
  3546.         tm.tm_mon = 0;
  3547.         }
  3548.     }
  3549.     tm.tm_mday = 1;
  3550.     }
  3551.     if (level > 5) {
  3552.     if (tm.tm_mon >= 7)
  3553.         tm.tm_year++;
  3554.     tm.tm_mon = 0;
  3555.     }
  3556.     ticplace = (double) gtimegm(&tm);
  3557.     ggmtime(&tm, (double) gtimegm(&tm));
  3558.     return (ticplace);
  3559. }
  3560.  
  3561. /* make smalltics for time-axis */
  3562. double make_ltic(tlevel, incr)
  3563. int tlevel;
  3564. double incr;
  3565. {
  3566.     double tinc;
  3567.     tinc = 0;
  3568.     if (tlevel < 0)
  3569.     tlevel = 0;
  3570.     switch (tlevel) {
  3571.     case 0:
  3572.     case 1:{
  3573.         if (incr >= 20)
  3574.         tinc = 10;
  3575.         if (incr >= 60)
  3576.         tinc = 30;
  3577.         if (incr >= 2 * 60)
  3578.         tinc = 60;
  3579.         if (incr >= 5 * 60)
  3580.         tinc = 2 * 60;
  3581.         if (incr >= 10 * 60)
  3582.         tinc = 5 * 60;
  3583.         if (incr >= 20 * 60)
  3584.         tinc = 10 * 60;
  3585.         break;
  3586.     }
  3587.     case 2:{
  3588.         if (incr >= 20 * 60)
  3589.         tinc = 10 * 60;
  3590.         if (incr >= 3600)
  3591.         tinc = 30 * 60;
  3592.         if (incr >= 2 * 3600)
  3593.         tinc = 3600;
  3594.         if (incr >= 5 * 3600)
  3595.         tinc = 2 * 3600;
  3596.         if (incr >= 10 * 3600)
  3597.         tinc = 5 * 3600;
  3598.         if (incr >= 20 * 3600)
  3599.         tinc = 10 * 3600;
  3600.         break;
  3601.     }
  3602.     case 3:{
  3603.         if (incr > 2 * 3600)
  3604.         tinc = 3600;
  3605.         if (incr > 4 * 3600)
  3606.         tinc = 2 * 3600;
  3607.         if (incr > 7 * 3600)
  3608.         tinc = 3 * 3600;
  3609.         if (incr > 13 * 3600)
  3610.         tinc = 6 * 3600;
  3611.         if (incr > DAY_SEC)
  3612.         tinc = 12 * 3600;
  3613.         if (incr > 2 * DAY_SEC)
  3614.         tinc = DAY_SEC;
  3615.         break;
  3616.     }
  3617.     case 4:{            /* week: tic per day */
  3618.         if (incr > 2 * DAY_SEC)
  3619.         tinc = DAY_SEC;
  3620.         if (incr > 7 * DAY_SEC)
  3621.         tinc = 7 * DAY_SEC;
  3622.         break;
  3623.     }
  3624.     case 5:{            /* month */
  3625.         if (incr > 2 * DAY_SEC)
  3626.         tinc = DAY_SEC;
  3627.         if (incr > 15 * DAY_SEC)
  3628.         tinc = 10 * DAY_SEC;
  3629.         if (incr > 2 * MON_SEC)
  3630.         tinc = MON_SEC;
  3631.         if (incr > 6 * MON_SEC)
  3632.         tinc = 3 * MON_SEC;
  3633.         if (incr > 2 * YEAR_SEC)
  3634.         tinc = YEAR_SEC;
  3635.         break;
  3636.     }
  3637.     case 6:{            /* year */
  3638.         if (incr > 2 * MON_SEC)
  3639.         tinc = MON_SEC;
  3640.         if (incr > 6 * MON_SEC)
  3641.         tinc = 3 * MON_SEC;
  3642.         if (incr > 2 * YEAR_SEC)
  3643.         tinc = YEAR_SEC;
  3644.         if (incr > 10 * YEAR_SEC)
  3645.         tinc = 5 * YEAR_SEC;
  3646.         if (incr > 50 * YEAR_SEC)
  3647.         tinc = 10 * YEAR_SEC;
  3648.         if (incr > 100 * YEAR_SEC)
  3649.         tinc = 20 * YEAR_SEC;
  3650.         if (incr > 200 * YEAR_SEC)
  3651.         tinc = 50 * YEAR_SEC;
  3652.         if (incr > 300 * YEAR_SEC)
  3653.         tinc = 100 * YEAR_SEC;
  3654.         break;
  3655.     }
  3656.     }
  3657.     return (tinc);
  3658. }
  3659.  
  3660.  
  3661. void write_multiline(x, y, text, hor, vert, angle, font)
  3662. unsigned int x, y;
  3663. char *text;
  3664. enum JUSTIFY hor;        /* horizontal ... */
  3665. int vert;            /* ... and vertical just - text in hor direction despite angle */
  3666. int angle;            /* assume term has already been set for this */
  3667. char *font;            /* NULL or "" means use default */
  3668. {
  3669.     register struct termentry *t = term;
  3670.     char *p = text;
  3671.  
  3672.     if (!p)
  3673.     return;
  3674.  
  3675.     if (vert != JUST_TOP) {
  3676.     /* count lines and adjust y */
  3677.     int lines = 0;        /* number of linefeeds - one fewer than lines */
  3678.     while (*p++) {
  3679.         if (*p == '\n')
  3680.         ++lines;
  3681.     }
  3682.     if (angle)
  3683.         x -= (vert * lines * t->v_char) / 2;
  3684.     else
  3685.         y += (vert * lines * t->v_char) / 2;
  3686.     }
  3687.     if (font && *font)
  3688.     (*t->set_font) (font);
  3689.  
  3690.  
  3691.     for (;;) {            /* we will explicitly break out */
  3692.  
  3693.     if ((text != NULL) && (p = strchr(text, '\n')) != NULL)
  3694.         *p = 0;        /* terminate the string */
  3695.  
  3696.     if ((*t->justify_text) (hor)) {
  3697.         (*t->put_text) (x, y, text);
  3698.     } else {
  3699.         int fix = hor * (t->h_char) * strlen(text) / 2;
  3700.         if (angle)
  3701.         (*t->put_text) (x, y - fix, text);
  3702.         else
  3703.         (*t->put_text) (x - fix, y, text);
  3704.     }
  3705.     if (angle)
  3706.         x += t->v_char;
  3707.     else
  3708.         y -= t->v_char;
  3709.  
  3710.     if (!p)
  3711.         break;
  3712.     else {
  3713.         /* put it back */
  3714.         *p = '\n';
  3715.     }
  3716.  
  3717.     text = p + 1;
  3718.     }                /* unconditional branch back to the for(;;) - just a goto ! */
  3719.  
  3720.     if (font && *font)
  3721.     (*t->set_font) (default_font);
  3722.  
  3723. }
  3724.  
  3725. /* display a x-axis ticmark - called by gen_ticks */
  3726. /* also uses global tic_start, tic_direction, tic_text and tic_just */
  3727. static void xtick2d_callback(axis, place, text, grid)
  3728. int axis;
  3729. double place;
  3730. char *text;
  3731. struct lp_style_type grid;    /* linetype or -2 for no grid */
  3732. {
  3733.     register struct termentry *t = term;
  3734.     /* minitick if text is NULL - beware - h_tic is unsigned */
  3735.     int ticsize = tic_direction * (int) (t->v_tic) * (text ? ticscale : miniticscale);
  3736.     unsigned int x = map_x(place);
  3737.  
  3738.     if (grid.l_type > -2) {
  3739.     term_apply_lp_properties(&grid);
  3740.     if (polar_grid_angle) {
  3741.         double x = place, y = 0, s = sin(0.1), c = cos(0.1);
  3742.         int i;
  3743.         int ogx = map_x(x);
  3744.         int ogy = map_y(0);
  3745.         int tmpgx, tmpgy, gx, gy;
  3746.  
  3747.         if (place > largest_polar_circle)
  3748.         largest_polar_circle = place;
  3749.         else if (-place > largest_polar_circle)
  3750.         largest_polar_circle = -place;
  3751.         for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
  3752.         {
  3753.             /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
  3754.             double tx = x * c - y * s;
  3755.             /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
  3756.             y = y * c + x * s;
  3757.             x = tx;
  3758.         }
  3759.         tmpgx = gx = map_x(x);
  3760.         tmpgy = gy = map_y(y);
  3761.         if (clip_line(&ogx, &ogy, &tmpgx, &tmpgy)) {
  3762.             (*t->move) ((unsigned int) ogx, (unsigned int) ogy);
  3763.             (*t->vector) ((unsigned int) tmpgx, (unsigned int) tmpgy);
  3764.         }
  3765.         ogx = gx;
  3766.         ogy = gy;
  3767.         }
  3768.     } else {
  3769.         if (lkey && key_yt > ybot && x < key_xr && x > key_xl) {
  3770.         if (key_yb > ybot) {
  3771.             (*t->move) (x, ybot);
  3772.             (*t->vector) (x, key_yb);
  3773.         }
  3774.         if (key_yt < ytop) {
  3775.             (*t->move) (x, key_yt);
  3776.             (*t->vector) (x, ytop);
  3777.         }
  3778.         } else {
  3779.         (*t->move) (x, ybot);
  3780.         (*t->vector) (x, ytop);
  3781.         }
  3782.     }
  3783.     term_apply_lp_properties(&border_lp);    /* border linetype */
  3784.     }
  3785.     /* we precomputed tic posn and text posn in global vars */
  3786.  
  3787.     (*t->move) (x, tic_start);
  3788.     (*t->vector) (x, tic_start + ticsize);
  3789.  
  3790.     if (tic_mirror >= 0) {
  3791.     (*t->move) (x, tic_mirror);
  3792.     (*t->vector) (x, tic_mirror - ticsize);
  3793.     }
  3794.     if (text)
  3795.     write_multiline(x, tic_text, text, tic_hjust, tic_vjust, rotate_tics, NULL);
  3796. }
  3797.  
  3798. /* display a y-axis ticmark - called by gen_ticks */
  3799. /* also uses global tic_start, tic_direction, tic_text and tic_just */
  3800. static void ytick2d_callback(axis, place, text, grid)
  3801. int axis;
  3802. double place;
  3803. char *text;
  3804. struct lp_style_type grid;    /* linetype or -2 */
  3805. {
  3806.     register struct termentry *t = term;
  3807.     /* minitick if text is NULL - v_tic is unsigned */
  3808.     int ticsize = tic_direction * (int) (t->h_tic) * (text ? ticscale : miniticscale);
  3809.     unsigned int y = map_y(place);
  3810.     if (grid.l_type > -2) {
  3811.     term_apply_lp_properties(&grid);
  3812.     if (polar_grid_angle) {
  3813.         double x = 0, y = place, s = sin(0.1), c = cos(0.1);
  3814.         int i;
  3815.         if (place > largest_polar_circle)
  3816.         largest_polar_circle = place;
  3817.         else if (-place > largest_polar_circle)
  3818.         largest_polar_circle = -place;
  3819.         clip_move(map_x(x), map_y(y));
  3820.         for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
  3821.         {
  3822.             /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
  3823.             double tx = x * c - y * s;
  3824.             /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
  3825.             y = y * c + x * s;
  3826.             x = tx;
  3827.         }
  3828.         clip_vector(map_x(x), map_y(y));
  3829.         }
  3830.     } else {
  3831.         if (lkey && y < key_yt && y > key_yb && key_xl < xright /* catch TOUT */ ) {
  3832.         if (key_xl > xleft) {
  3833.             (*t->move) (xleft, y);
  3834.             (*t->vector) (key_xl, y);
  3835.         }
  3836.         if (key_xr < xright) {
  3837.             (*t->move) (key_xr, y);
  3838.             (*t->vector) (xright, y);
  3839.         }
  3840.         } else {
  3841.         (*t->move) (xleft, y);
  3842.         (*t->vector) (xright, y);
  3843.         }
  3844.     }
  3845.     term_apply_lp_properties(&border_lp);    /* border linetype */
  3846.     }
  3847.     /* we precomputed tic posn and text posn */
  3848.  
  3849.     (*t->move) (tic_start, y);
  3850.     (*t->vector) (tic_start + ticsize, y);
  3851.  
  3852.     if (tic_mirror >= 0) {
  3853.     (*t->move) (tic_mirror, y);
  3854.     (*t->vector) (tic_mirror - ticsize, y);
  3855.     }
  3856.     if (text)
  3857.     write_multiline(tic_text, y, text, tic_hjust, tic_vjust, rotate_tics, NULL);
  3858. }
  3859.  
  3860. int label_width(str, lines)
  3861. char *str;
  3862. int *lines;
  3863. {
  3864.     char lab[MAX_LINE_LEN + 1], *s, *e;
  3865.     int mlen, len, l;
  3866.  
  3867.     l = mlen = len = 0;
  3868.     sprintf(lab, "%s\n", str);
  3869.     s = lab;
  3870.     while ((e = (char *) strchr(s, '\n')) != NULL) {    /* HBB 980308: quiet BC-3.1 warning */
  3871.     *e = '\0';
  3872.     len = strlen(s);    /* = e-s ? */
  3873.     if (len > mlen)
  3874.         mlen = len;
  3875.     if (len || l)
  3876.         l++;
  3877.     s = ++e;
  3878.     }
  3879.     /* lines = NULL => not interested - div */
  3880.     if (lines)
  3881.     *lines = l;
  3882.     return (mlen);
  3883. }
  3884.  
  3885.  
  3886. void setup_tics(axis, ticdef, format, max)
  3887. int axis;
  3888. struct ticdef *ticdef;
  3889. char *format;
  3890. int max;            /* approx max number of slots available */
  3891. {
  3892.     double tic = 0;        /* HBB: shut up gcc -Wall */
  3893.  
  3894.     int fixmin = (auto_array[axis] & 1) != 0;
  3895.     int fixmax = (auto_array[axis] & 2) != 0;
  3896.  
  3897.     if (ticdef->type == TIC_SERIES) {
  3898.     ticstep[axis] = tic = ticdef->def.series.incr;
  3899.     fixmin &= (ticdef->def.series.start == -VERYLARGE);
  3900.     fixmax &= (ticdef->def.series.end == VERYLARGE);
  3901.     } else if (ticdef->type == TIC_COMPUTED) {
  3902.     ticstep[axis] = tic = make_tics(axis, max);
  3903.     } else {
  3904.     fixmin = fixmax = 0;    /* user-defined, day or month */
  3905.     }
  3906.  
  3907.     if (fixmin) {
  3908.     if (min_array[axis] < max_array[axis])
  3909.         min_array[axis] = tic * floor(min_array[axis] / tic);
  3910.     else
  3911.         min_array[axis] = tic * ceil(min_array[axis] / tic);
  3912.     }
  3913.     if (fixmax) {
  3914.     if (min_array[axis] < max_array[axis])
  3915.         max_array[axis] = tic * ceil(max_array[axis] / tic);
  3916.     else
  3917.         max_array[axis] = tic * floor(max_array[axis] / tic);
  3918.     }
  3919.     if (datatype[axis] == TIME && format_is_numeric[axis])
  3920.     /* invent one for them */
  3921.     timetic_format(axis, min_array[axis], max_array[axis]);
  3922.     else
  3923.     strcpy(ticfmt[axis], format);
  3924. }
  3925.  
  3926. /*{{{  mant_exp - split into mantissa and/or exponent */
  3927. static void mant_exp(log_base, x, scientific, m, p)
  3928. double log_base, x;
  3929. int scientific;            /* round to power of 3 */
  3930. double *m;
  3931. int *p;                /* results */
  3932. {
  3933.     int sign = 1;
  3934.     double l10;
  3935.     int power;
  3936.     /*{{{  check 0 */
  3937.     if (x == 0) {
  3938.     if (m)
  3939.         *m = 0;
  3940.     if (p)
  3941.         *p = 0;
  3942.     return;
  3943.     }
  3944.     /*}}} */
  3945.     /*{{{  check -ve */
  3946.     if (x < 0) {
  3947.     sign = (-1);
  3948.     x = (-x);
  3949.     }
  3950.     /*}}} */
  3951.  
  3952.     l10 = log10(x) / log_base;
  3953.     power = floor(l10);
  3954.     if (scientific) {
  3955.     power = 3 * floor(power / 3.0);
  3956.     }
  3957.     if (m)
  3958.     *m = sign * pow(10.0, (l10 - power) * log_base);
  3959.     if (p)
  3960.     *p = power;
  3961. }
  3962. /*}}} */
  3963.  
  3964. /*
  3965.  * Kludge alert!!
  3966.  * Workaround until we have a better solution ...
  3967.  * Note: this assumes that all calls to sprintf in gprintf have
  3968.  * exactly three args. Lars
  3969.  */
  3970. #ifdef HAVE_SNPRINTF
  3971. # define sprintf(str,fmt,arg) \
  3972.     if (snprintf((str),count,(fmt),(arg)) > count) \
  3973.       fprintf (stderr,"%s:%d: Warning: too many digits for format\n",__FILE__,__LINE__)
  3974. #endif
  3975.  
  3976. /*{{{  gprintf */
  3977. /* extended snprintf */
  3978. static void gprintf(dest, count, format, log_base, x)
  3979. char *dest, *format;
  3980. size_t count;
  3981. double log_base, x;        /* we print one number in a number of different formats */
  3982. {
  3983. #define LOC_PI 3.14159265358979323846    /* local definition of PI */
  3984.     char temp[MAX_LINE_LEN];
  3985.     char *t;
  3986.  
  3987.     for (;;) {
  3988.     /*{{{  copy to dest until % */
  3989.     while (*format != '%')
  3990.         if (!(*dest++ = *format++))
  3991.         return;        /* end of format */
  3992.     /*}}} */
  3993.  
  3994.     /*{{{  check for %% */
  3995.     if (format[1] == '%') {
  3996.         *dest++ = '%';
  3997.         format += 2;
  3998.         continue;
  3999.     }
  4000.     /*}}} */
  4001.  
  4002.     /*{{{  copy format part to temp, excluding conversion character */
  4003.     t = temp;
  4004.     *t++ = '%';
  4005.     /* dont put isdigit first since sideeffect in macro is bad */
  4006.     while (*++format == '.' || isdigit((int) *format) ||
  4007.            *format == '-' || *format == '+' || *format == ' ')
  4008.         *t++ = *format;
  4009.     /*}}} */
  4010.  
  4011.     /*{{{  convert conversion character */
  4012.     switch (*format) {
  4013.         /*{{{  x and o */
  4014.     case 'x':
  4015.     case 'X':
  4016.     case 'o':
  4017.     case 'O':
  4018.         t[0] = *format++;
  4019.         t[1] = 0;
  4020.         sprintf(dest, temp, (int) x);
  4021.         dest += strlen(dest);
  4022.         break;
  4023.         /*}}} */
  4024.         /*{{{  e, f and g */
  4025.     case 'e':
  4026.     case 'E':
  4027.     case 'f':
  4028.     case 'F':
  4029.     case 'g':
  4030.     case 'G':
  4031.         t[0] = *format++;
  4032.         t[1] = 0;
  4033.         sprintf(dest, temp, x);
  4034.         dest += strlen(dest);
  4035.         break;
  4036.         /*}}} */
  4037.         /*{{{  l */
  4038.     case 'l':
  4039.         {
  4040.         double mantissa;
  4041.         mant_exp(log_base, x, 0, &mantissa, NULL);
  4042.         t[0] = 'f';
  4043.         t[1] = 0;
  4044.         sprintf(dest, temp, mantissa);
  4045.         dest += strlen(dest);
  4046.         ++format;
  4047.         break;
  4048.         }
  4049.         /*}}} */
  4050.         /*{{{  t */
  4051.     case 't':
  4052.         {
  4053.         double mantissa;
  4054.         mant_exp(1.0, x, 0, &mantissa, NULL);
  4055.         t[0] = 'f';
  4056.         t[1] = 0;
  4057.         sprintf(dest, temp, mantissa);
  4058.         dest += strlen(dest);
  4059.         ++format;
  4060.         break;
  4061.         }
  4062.         /*}}} */
  4063.         /*{{{  s */
  4064.     case 's':
  4065.         {
  4066.         double mantissa;
  4067.         mant_exp(1.0, x, 1, &mantissa, NULL);
  4068.         t[0] = 'f';
  4069.         t[1] = 0;
  4070.         sprintf(dest, temp, mantissa);
  4071.         dest += strlen(dest);
  4072.         ++format;
  4073.         break;
  4074.         }
  4075.         /*}}} */
  4076.         /*{{{  L */
  4077.     case 'L':
  4078.         {
  4079.         int power;
  4080.         mant_exp(log_base, x, 0, NULL, &power);
  4081.         t[0] = 'd';
  4082.         t[1] = 0;
  4083.         sprintf(dest, temp, power);
  4084.         dest += strlen(dest);
  4085.         ++format;
  4086.         break;
  4087.         }
  4088.         /*}}} */
  4089.         /*{{{  T */
  4090.     case 'T':
  4091.         {
  4092.         int power;
  4093.         mant_exp(1.0, x, 0, NULL, &power);
  4094.         t[0] = 'd';
  4095.         t[1] = 0;
  4096.         sprintf(dest, temp, power);
  4097.         dest += strlen(dest);
  4098.         ++format;
  4099.         break;
  4100.         }
  4101.         /*}}} */
  4102.         /*{{{  S */
  4103.     case 'S':
  4104.         {
  4105.         int power;
  4106.         mant_exp(1.0, x, 1, NULL, &power);
  4107.         t[0] = 'd';
  4108.         t[1] = 0;
  4109.         sprintf(dest, temp, power);
  4110.         dest += strlen(dest);
  4111.         ++format;
  4112.         break;
  4113.         }
  4114.         /*}}} */
  4115.         /*{{{  c */
  4116.     case 'c':
  4117.         {
  4118.         int power;
  4119.         mant_exp(1.0, x, 1, NULL, &power);
  4120.         t[0] = 'c';
  4121.         t[1] = 0;
  4122.         power = power / 3 + 6;    /* -18 -> 0, 0 -> 6, +18 -> 12, ... */
  4123.         if (power >= 0 && power <= 12) {
  4124.             sprintf(dest, temp, "afpnum kMGTPE"[power]);
  4125.         } else {
  4126.             /* please extend the range ! */
  4127.             /* name  power   name  power
  4128.                -------------------------
  4129.                atto   -18    Exa    18
  4130.                femto  -15    Peta   15
  4131.                pico   -12    Tera   12
  4132.                nano    -9    Giga    9
  4133.                micro   -6    Mega    6
  4134.                milli   -3    kilo    3   */
  4135.  
  4136.             /* for the moment, print e+21 for example */
  4137.             sprintf(dest, "e%+02d", (power - 6) * 3);
  4138.         }
  4139.  
  4140.         dest += strlen(dest);
  4141.         ++format;
  4142.         break;
  4143.         }
  4144.     case 'P':
  4145.         {
  4146.         t[0] = 'f';
  4147.         t[1] = 0;
  4148.         sprintf(dest, temp, x / LOC_PI);
  4149.         dest += strlen(dest);
  4150.         ++format;
  4151.         break;
  4152.         }
  4153.         /*}}} */
  4154.     default:
  4155.         int_error("Bad format character", NO_CARET);
  4156.     }
  4157.     /*}}} */
  4158.     }
  4159. }
  4160. #undef LOC_PI            /* local definition of PI */
  4161. /*}}} */
  4162. #ifdef HAVE_SNPRINTF
  4163. # undef sprintf
  4164. #endif
  4165.  
  4166.  
  4167.  
  4168. /*{{{  gen_tics */
  4169. /* uses global arrays ticstep[], ticfmt[], min_array[], max_array[],
  4170.  * auto_array[], log_array[], log_base_array[]
  4171.  * we use any of GRID_X/Y/X2/Y2 and  _MX/_MX2/etc - caller is expected
  4172.  * to clear the irrelevent fields from global grid bitmask
  4173.  * note this is also called from graph3d, so we need GRID_Z too
  4174.  */
  4175.  
  4176. void gen_tics(axis, def, grid, minitics, minifreq, callback)
  4177. int axis;            /* FIRST_X_AXIS, etc */
  4178. struct ticdef *def;        /* tic defn */
  4179. int grid;            /* GRID_X | GRID_MX etc */
  4180. int minitics;            /* minitics - off/default/auto/explicit */
  4181. double minifreq;        /* frequency */
  4182. tic_callback callback;        /* fn to call to actually do the work */
  4183. {
  4184.     /* seperate main-tic part of grid */
  4185.     struct lp_style_type lgrd, mgrd;
  4186.  
  4187.     memcpy(&lgrd, &grid_lp, sizeof(struct lp_style_type));
  4188.     memcpy(&mgrd, &mgrid_lp, sizeof(struct lp_style_type));
  4189.     lgrd.l_type = (grid & (GRID_X | GRID_Y | GRID_X2 | GRID_Y2 | GRID_Z)) ? grid_lp.l_type : -2;
  4190.     mgrd.l_type = (grid & (GRID_MX | GRID_MY | GRID_MX2 | GRID_MY2 | GRID_MZ)) ? mgrid_lp.l_type : -2;
  4191.  
  4192.     if (def->type == TIC_USER) {    /* special case */
  4193.     /*{{{  do user tics then return */
  4194.     struct ticmark *mark = def->def.user;
  4195.     double uncertain = (max_array[axis] - min_array[axis]) / 10;
  4196.     double ticmin = min_array[axis] - SIGNIF * uncertain;
  4197.     double internal_max = max_array[axis] + SIGNIF * uncertain;
  4198.     double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
  4199.  
  4200.     /* polar labels always +ve, and if rmin has been set, they are
  4201.      * relative to rmin. position is as user specified, but must
  4202.      * be translated. I dont think it will work at all for
  4203.      * log scale, so I shan't worry about it !
  4204.      */
  4205.     double polar_shift = (polar && !(autoscale_r & 1)) ? rmin : 0;
  4206.  
  4207.     for (mark = def->def.user; mark; mark = mark->next) {
  4208.         char label[64];
  4209.         double internal = log_array[axis] ? log(mark->position) / log_base_array[axis] : mark->position;
  4210.  
  4211.         internal -= polar_shift;
  4212.  
  4213.         if (!inrange(internal, ticmin, internal_max))
  4214.         continue;
  4215.  
  4216.         if (datatype[axis] == TIME)
  4217.         gstrftime(label, 24, mark->label ? mark->label : ticfmt[axis], mark->position);
  4218.         else
  4219.         gprintf(label, sizeof(label), mark->label ? mark->label : ticfmt[axis], log_base, mark->position);
  4220.  
  4221.         (*callback) (axis, internal, label, lgrd);
  4222.     }
  4223.  
  4224.     return;            /* NO MINITICS FOR USER-DEF TICS */
  4225.     /*}}} */
  4226.     }
  4227.     /* series-tics
  4228.      * need to distinguish user co-ords from internal co-ords.
  4229.      * - for logscale, internal = log(user), else internal = user
  4230.      *
  4231.      * The minitics are a bit of a drag - we need to distinuish
  4232.      * the cases step>1 from step == 1.
  4233.      * If step = 1, we are looking at 1,10,100,1000 for example, so
  4234.      * minitics are 2,5,8, ...  - done in user co-ordinates
  4235.      * If step>1, we are looking at 1,1e6,1e12 for example, so
  4236.      * minitics are 10,100,1000,... - done in internal co-ords
  4237.      */
  4238.  
  4239.     {
  4240.     double tic;        /* loop counter */
  4241.     double internal;    /* in internal co-ords */
  4242.     double user;        /* in user co-ords */
  4243.     double start, step, end;
  4244.     double lmin = min_array[axis], lmax = max_array[axis];
  4245.     double internal_min, internal_max;    /* to allow for rounding errors */
  4246.     double ministart = 0, ministep = 1, miniend = 1;    /* internal or user - depends on step */
  4247.     int anyticput = 0;    /* for detection of infinite loop */
  4248.  
  4249.     /* gprintf uses log10() of base - log_base_array is log() */
  4250.     double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
  4251.  
  4252.     if (lmax < lmin) {
  4253.         /* hmm - they have set reversed range for some reason */
  4254.         double temp = lmin;
  4255.         lmin = lmax;
  4256.         lmax = temp;
  4257.     }
  4258.     /*{{{  choose start, step and end */
  4259.     switch (def->type) {
  4260.     case TIC_SERIES:
  4261.         if (log_array[axis]) {
  4262.         /* we can tolerate start <= 0 if step and end > 0 */
  4263.         if (def->def.series.end <= 0 ||
  4264.             def->def.series.incr <= 0)
  4265.             return;    /* just quietly ignore */
  4266.         step = log(def->def.series.incr) / log_base_array[axis];
  4267.         end = log(def->def.series.end) / log_base_array[axis];
  4268.         start = def->def.series.start > 0 ?
  4269.             log(def->def.series.start) / log_base_array[axis] :
  4270.             step;
  4271.         } else {
  4272.         start = def->def.series.start;
  4273.         step = def->def.series.incr;
  4274.         end = def->def.series.end;
  4275.         if (start == -VERYLARGE)
  4276.             start = step * floor(lmin / step);
  4277.         if (end == VERYLARGE)
  4278.             end = step * ceil(lmax / step);
  4279.         }
  4280.         break;
  4281.     case TIC_COMPUTED:
  4282.         /* round to multiple of step */
  4283.         start = ticstep[axis] * floor(lmin / ticstep[axis]);
  4284.         step = ticstep[axis];
  4285.         end = ticstep[axis] * ceil(lmax / ticstep[axis]);
  4286.         break;
  4287.     case TIC_MONTH:
  4288.         start = floor(lmin);
  4289.         end = ceil(lmax);
  4290.         step = floor((end - start) / 12);
  4291.         if (step < 1)
  4292.         step = 1;
  4293.         break;
  4294.     case TIC_DAY:
  4295.         start = floor(lmin);
  4296.         end = ceil(lmax);
  4297.         step = floor((end - start) / 14);
  4298.         if (step < 1)
  4299.         step = 1;
  4300.         break;
  4301.     default:
  4302.         graph_error("Internal error : unknown tic type");
  4303.         return;        /* avoid gcc -Wall warning about start */
  4304.     }
  4305.     /*}}} */
  4306.  
  4307.     /*{{{  ensure ascending order */
  4308.     if (end < start) {
  4309.         double temp;
  4310.         temp = end;
  4311.         end = start;
  4312.         start = temp;
  4313.     }
  4314.     step = fabs(step);
  4315.     /*}}} */
  4316.  
  4317.     if (minitics) {
  4318.         /*{{{  figure out ministart, ministep, miniend */
  4319.         if (minitics == MINI_USER) {
  4320.         /* they have said what they want */
  4321.         if (minifreq <= 0)
  4322.             minitics = 0;    /* not much else we can do */
  4323.         else if (log_array[axis]) {
  4324.             ministart = ministep = step / minifreq * base_array[axis];
  4325.             miniend = step * base_array[axis];
  4326.         } else {
  4327.             ministart = ministep = step / minifreq;
  4328.             miniend = step;
  4329.         }
  4330.         } else if (log_array[axis]) {
  4331.         if (step > 1.5) {    /* beware rounding errors */
  4332.             /*{{{  10,100,1000 case */
  4333.             /* no more than five minitics */
  4334.             ministart = ministep = (int) (0.2 * step);
  4335.             if (ministep < 1)
  4336.             ministart = ministep = 1;
  4337.             miniend = step;
  4338.             /*}}} */
  4339.         } else {
  4340.             /*{{{  2,5,8 case */
  4341.             miniend = base_array[axis];
  4342.             if (end - start >= 10)
  4343.             minitics = 0;    /* none */
  4344.             else if (end - start >= 5) {
  4345.             ministart = 2;
  4346.             ministep = 3;
  4347.             } else {
  4348.             ministart = 2;
  4349.             ministep = 1;
  4350.             }
  4351.             /*}}} */
  4352.         }
  4353.         } else if (datatype[axis] == TIME) {
  4354.         ministart = ministep = make_ltic(timelevel[axis], step);
  4355.         miniend = step * 0.9;
  4356.         } else if (minitics == MINI_AUTO) {
  4357.         ministart = ministep = 0.1 * step;
  4358.         miniend = step;
  4359.         } else
  4360.         minitics = 0;
  4361.  
  4362.         if (ministep <= 0)
  4363.         minitics = 0;    /* dont get stuck in infinite loop */
  4364.         /*}}} */
  4365.     }
  4366.     /*{{{  a few tweaks and checks */
  4367.     /* watch rounding errors */
  4368.     end += SIGNIF * step;
  4369.     internal_max = lmax + step * SIGNIF;
  4370.     internal_min = lmin - step * SIGNIF;
  4371.  
  4372.     if (step == 0)
  4373.         return;        /* just quietly ignore them ! */
  4374.     /*}}} */
  4375.  
  4376.     for (tic = start; tic <= end; tic += step) {
  4377.         if (anyticput == 2)    /* See below... */
  4378.         break;
  4379.         if (anyticput && (fabs(tic - start) < DBL_EPSILON)) {
  4380.         /* step is too small.. */
  4381.         anyticput = 2;    /* Don't try again. */
  4382.         tic = end;    /* Put end tic. */
  4383.         } else
  4384.         anyticput = 1;
  4385.  
  4386.         /*{{{  calc internal and user co-ords */
  4387.         if (!log_array[axis]) {
  4388.         internal = datatype[axis] == TIME ? time_tic_just(timelevel[axis], tic) : tic;
  4389.         user = CheckZero(internal, step);
  4390.         } else {
  4391.         /* log scale => dont need to worry about zero ? */
  4392.         internal = tic;
  4393.         user = pow(base_array[axis], internal);
  4394.         }
  4395.         /*}}} */
  4396.         if (internal > internal_max)
  4397.         break;        /* gone too far - end of series = VERYLARGE perhaps */
  4398.         if (internal >= internal_min) {
  4399. /* continue; *//* maybe minitics!!!. user series starts below min ? */
  4400.  
  4401.         /*{{{  draw tick via callback */
  4402.         switch (def->type) {
  4403.         case TIC_DAY:{
  4404.             int d = (long) floor(user + 0.5) % 7;
  4405.             if (d < 0)
  4406.                 d += 7;
  4407.             (*callback) (axis, internal, abbrev_day_names[d], lgrd);
  4408.             break;
  4409.             }
  4410.         case TIC_MONTH:{
  4411.             int m = (long) floor(user - 1) % 12;
  4412.             if (m < 0)
  4413.                 m += 12;
  4414.             (*callback) (axis, internal, abbrev_month_names[m], lgrd);
  4415.             break;
  4416.             }
  4417.         default:{    /* comp or series */
  4418.             char label[64];
  4419.             if (datatype[axis] == TIME) {
  4420.                 /* If they are doing polar time plot, good luck to them */
  4421.                 gstrftime(label, 24, ticfmt[axis], (double) user);
  4422.             } else if (polar) {
  4423.                 /* if rmin is set, we stored internally with r-rmin */
  4424. /* Igor's polar-grid patch */
  4425. #if 1
  4426.                 /* HBB 990327: reverted to 'pre-Igor' version... */
  4427.                 double r = fabs(user) + (autoscale_r & 1 ? 0 : rmin);
  4428. #else
  4429.                 /* Igor removed fabs to allow -ve labels */
  4430.                 double r = user + (autoscale_r & 1 ? 0 : rmin);
  4431. #endif
  4432.                 gprintf(label, sizeof(label), ticfmt[axis], log_base, r);
  4433.             } else {
  4434.                 gprintf(label, sizeof(label), ticfmt[axis], log_base, user);
  4435.             }
  4436.             (*callback) (axis, internal, label, lgrd);
  4437.             }
  4438.         }
  4439.         /*}}} */
  4440.  
  4441.         }
  4442.         if (minitics) {
  4443.         /*{{{  process minitics */
  4444.         double mplace, mtic;
  4445.         for (mplace = ministart; mplace < miniend; mplace += ministep) {
  4446.             if (datatype[axis] == TIME)
  4447.             mtic = time_tic_just(timelevel[axis] - 1, internal + mplace);
  4448.             else
  4449.             mtic = internal + (log_array[axis] && step <= 1.5 ? log(mplace) / log_base_array[axis] : mplace);
  4450.             if (inrange(mtic, internal_min, internal_max) &&
  4451.             inrange(mtic, start - step * SIGNIF, end + step * SIGNIF))
  4452.             (*callback) (axis, mtic, NULL, mgrd);
  4453.         }
  4454.         /*}}} */
  4455.         }
  4456.     }
  4457.     }
  4458. }
  4459. /*}}} */
  4460.  
  4461. /*{{{  map_position */
  4462. static void map_position(pos, x, y, what)
  4463. struct position *pos;
  4464. unsigned int *x, *y;
  4465. char *what;
  4466. {
  4467.     switch (pos->scalex) {
  4468.     case first_axes:
  4469.     {
  4470.         double xx = LogScale(pos->x, log_array[FIRST_X_AXIS], log_base_array[FIRST_X_AXIS], what, "x");
  4471.         *x = xleft + (xx - min_array[FIRST_X_AXIS]) * scale[FIRST_X_AXIS] + 0.5;
  4472.         break;
  4473.     }
  4474.     case second_axes:
  4475.     {
  4476.         double xx = LogScale(pos->x, log_array[SECOND_X_AXIS], log_base_array[SECOND_X_AXIS], what, "x");
  4477.         *x = xleft + (xx - min_array[SECOND_X_AXIS]) * scale[SECOND_X_AXIS] + 0.5;
  4478.         break;
  4479.     }
  4480.     case graph:
  4481.     {
  4482.         *x = xleft + pos->x * (xright - xleft) + 0.5;
  4483.         break;
  4484.     }
  4485.     case screen:
  4486.     {
  4487.         register struct termentry *t = term;
  4488.         *x = pos->x * (t->xmax) + 0.5;
  4489.         break;
  4490.     }
  4491.     }
  4492.     switch (pos->scaley) {
  4493.     case first_axes:
  4494.     {
  4495.         double yy = LogScale(pos->y, log_array[FIRST_Y_AXIS], log_base_array[FIRST_Y_AXIS], what, "y");
  4496.         *y = ybot + (yy - min_array[FIRST_Y_AXIS]) * scale[FIRST_Y_AXIS] + 0.5;
  4497.         return;
  4498.     }
  4499.     case second_axes:
  4500.     {
  4501.         double yy = LogScale(pos->y, log_array[SECOND_Y_AXIS], log_base_array[SECOND_Y_AXIS], what, "y");
  4502.         *y = ybot + (yy - min_array[SECOND_Y_AXIS]) * scale[SECOND_Y_AXIS] + 0.5;
  4503.         return;
  4504.     }
  4505.     case graph:
  4506.     {
  4507.         *y = ybot + pos->y * (ytop - ybot) + 0.5;
  4508.         return;
  4509.     }
  4510.     case screen:
  4511.     {
  4512.         register struct termentry *t = term;
  4513.         *y = pos->y * (t->ymax) + 0.5;
  4514.         return;
  4515.     }
  4516.     }
  4517. }
  4518. /*}}} */
  4519.